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 == 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 == 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 == 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 == 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 == 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 == 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 == 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 == 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 if (note_types_per_minute() == end_npm || _type == Constant) {
433 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
434 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
437 /** compute the function constant from some tempo-time point.
438 * @param end_npm tempo (note types/min.)
439 * @param end_minute distance (in minutes) from session origin
440 * @return the calculated function constant
443 TempoSection::compute_c_func_minute (const double& end_npm, const double& end_minute) const
445 if (note_types_per_minute() == end_npm || _type == Constant) {
449 return c_func (end_npm, end_minute - minute());
452 /* position function */
454 TempoSection::a_func (double end_npm, double c) const
456 return log (end_npm / note_types_per_minute()) / c;
459 /*function constant*/
461 TempoSection::c_func (double end_npm, double end_time) const
463 return log (end_npm / note_types_per_minute()) / end_time;
466 /* tempo in note types per minute at time in minutes */
468 TempoSection::_tempo_at_time (const double& time) const
470 return exp (_c * time) * note_types_per_minute();
473 /* time in minutes at tempo in note types per minute */
475 TempoSection::_time_at_tempo (const double& npm) const
477 return log (npm / note_types_per_minute()) / _c;
480 /* pulse at tempo in note types per minute */
482 TempoSection::_pulse_at_tempo (const double& npm) const
484 return ((npm - note_types_per_minute()) / _c) / _note_type;
487 /* tempo in note types per minute at pulse */
489 TempoSection::_tempo_at_pulse (const double& pulse) const
491 return (pulse * _note_type * _c) + note_types_per_minute();
494 /* pulse at time in minutes */
496 TempoSection::_pulse_at_time (const double& time) const
498 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
501 /* time in minutes at pulse */
503 TempoSection::_time_at_pulse (const double& pulse) const
505 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
508 /***********************************************************************/
510 const string MeterSection::xml_state_node_name = "Meter";
512 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
513 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
515 XMLProperty const * prop;
520 framepos_t frame = 0;
521 pair<double, BBT_Time> start;
523 if ((prop = node.property ("start")) != 0) {
524 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
528 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
530 /* legacy session - start used to be in bbt*/
531 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
536 if ((prop = node.property ("pulse")) != 0) {
537 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
538 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
543 if ((prop = node.property ("beat")) != 0) {
544 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
545 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
551 if ((prop = node.property ("bbt")) == 0) {
552 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
553 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
557 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
558 throw failed_constructor();
564 if ((prop = node.property ("frame")) != 0) {
565 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
566 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
567 throw failed_constructor();
569 set_minute (minute_at_frame (frame));
573 /* beats-per-bar is old; divisions-per-bar is new */
575 if ((prop = node.property ("divisions-per-bar")) == 0) {
576 if ((prop = node.property ("beats-per-bar")) == 0) {
577 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
578 throw failed_constructor();
581 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
582 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
583 throw failed_constructor();
586 if ((prop = node.property ("note-type")) == 0) {
587 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
588 throw failed_constructor();
590 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
591 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
592 throw failed_constructor();
595 if ((prop = node.property ("movable")) == 0) {
596 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
597 throw failed_constructor();
600 set_initial (!string_is_affirmative (prop->value()));
602 if ((prop = node.property ("lock-style")) == 0) {
603 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
605 set_position_lock_style (MusicTime);
607 set_position_lock_style (AudioTime);
610 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
615 MeterSection::get_state() const
617 XMLNode *root = new XMLNode (xml_state_node_name);
621 snprintf (buf, sizeof (buf), "%lf", pulse());
622 root->add_property ("pulse", buf);
623 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
627 root->add_property ("bbt", buf);
628 snprintf (buf, sizeof (buf), "%lf", beat());
629 root->add_property ("beat", buf);
630 snprintf (buf, sizeof (buf), "%lf", _note_type);
631 root->add_property ("note-type", buf);
632 snprintf (buf, sizeof (buf), "%li", frame());
633 root->add_property ("frame", buf);
634 root->add_property ("lock-style", enum_2_string (position_lock_style()));
635 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
636 root->add_property ("divisions-per-bar", buf);
637 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
638 root->add_property ("movable", buf);
643 /***********************************************************************/
647 Tempo determines the rate of musical pulse determined by its components
648 note types per minute - the rate per minute of the whole note divisor _note_type
649 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
650 Meter divides the musical pulse into measures and beats according to its components
654 TempoSection - translates between time, musical pulse and tempo.
655 has a musical location in whole notes (pulses).
656 has a time location in minutes.
657 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
658 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
660 MeterSection - translates between BBT, meter-based beat and musical pulse.
661 has a musical location in whole notes (pulses)
662 has a musical location in meter-based beats
663 has a musical location in BBT time
664 has a time location expressed in minutes.
666 TempoSection and MeterSection may be locked to either audio or music (position lock style).
667 The lock style determines the location type to be kept as a reference when location is recalculated.
669 The first tempo and meter are special. they must move together, and are locked to audio.
670 Audio locked tempi which lie before the first meter are made inactive.
672 Recomputing the map is the process where the 'missing' location types are calculated.
673 We construct the tempo map by first using the locked location type of each section
674 to determine non-locked location types (pulse or minute position).
675 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
677 Having done this, we can now traverse the Metrics list by pulse or minute
678 to query its relevant meter/tempo.
680 It is important to keep the _metrics in an order that makes sense.
681 Because ramped MusicTime and AudioTime tempos can interact with each other,
682 reordering is frequent. Care must be taken to keep _metrics in a solved state.
683 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
687 Music and audio-locked objects may seem interchangeable on the surface, but when translating
688 between audio samples and beat, remember that a sample is only a quantised approximation
689 of the actual time (in minutes) of a beat.
690 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
691 mean that this frame is the actual location in time of 1|3|0.
693 You cannot use a frame measurement to determine beat distance except under special circumstances
694 (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).
696 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
697 sample space the user is operating at to be translated correctly to the object.
699 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
700 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
701 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
703 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
705 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
706 The result is rounded to audio frames.
707 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
710 frame_at_beat (beat_at_frame (frame)) == frame
712 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
714 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
715 frames_between_quarter_notes () eliminats this effect when determining time duration
716 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
718 The above pointless example could instead do:
719 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
721 The Shaggs - Things I Wonder
722 https://www.youtube.com/watch?v=9wQK6zMJOoQ
725 struct MetricSectionSorter {
726 bool operator() (const MetricSection* a, const MetricSection* b) {
727 return a->pulse() < b->pulse();
731 struct MetricSectionFrameSorter {
732 bool operator() (const MetricSection* a, const MetricSection* b) {
733 return a->frame() < b->frame();
737 TempoMap::TempoMap (framecnt_t fr)
740 BBT_Time start (1, 1, 0);
742 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.note_types_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime, fr);
743 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
745 t->set_initial (true);
746 t->set_locked_to_meter (true);
748 m->set_initial (true);
750 /* note: frame time is correct (zero) for both of these */
752 _metrics.push_back (t);
753 _metrics.push_back (m);
757 TempoMap::TempoMap (TempoMap const & other)
759 _frame_rate = other._frame_rate;
760 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
761 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
762 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
765 TempoSection* new_section = new TempoSection (*ts);
766 _metrics.push_back (new_section);
768 MeterSection* new_section = new MeterSection (*ms);
769 _metrics.push_back (new_section);
775 TempoMap::operator= (TempoMap const & other)
777 if (&other != this) {
778 _frame_rate = other._frame_rate;
780 Metrics::const_iterator d = _metrics.begin();
781 while (d != _metrics.end()) {
787 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
788 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
789 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
792 TempoSection* new_section = new TempoSection (*ts);
793 _metrics.push_back (new_section);
795 MeterSection* new_section = new MeterSection (*ms);
796 _metrics.push_back (new_section);
801 PropertyChanged (PropertyChange());
806 TempoMap::~TempoMap ()
808 Metrics::const_iterator d = _metrics.begin();
809 while (d != _metrics.end()) {
817 TempoMap::frame_at_minute (const double time) const
819 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
823 TempoMap::minute_at_frame (const framepos_t frame) const
825 return (frame / (double) _frame_rate) / 60.0;
829 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
831 bool removed = false;
834 Glib::Threads::RWLock::WriterLock lm (lock);
835 if ((removed = remove_tempo_locked (tempo))) {
836 if (complete_operation) {
837 recompute_map (_metrics);
842 if (removed && complete_operation) {
843 PropertyChanged (PropertyChange ());
848 TempoMap::remove_tempo_locked (const TempoSection& tempo)
852 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
853 if (dynamic_cast<TempoSection*> (*i) != 0) {
854 if (tempo.frame() == (*i)->frame()) {
855 if (!(*i)->initial()) {
868 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
870 bool removed = false;
873 Glib::Threads::RWLock::WriterLock lm (lock);
874 if ((removed = remove_meter_locked (tempo))) {
875 if (complete_operation) {
876 recompute_map (_metrics);
881 if (removed && complete_operation) {
882 PropertyChanged (PropertyChange ());
887 TempoMap::remove_meter_locked (const MeterSection& meter)
890 if (meter.position_lock_style() == AudioTime) {
891 /* remove meter-locked tempo */
892 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
894 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
895 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
904 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
905 if (dynamic_cast<MeterSection*> (*i) != 0) {
906 if (meter.frame() == (*i)->frame()) {
907 if (!(*i)->initial()) {
920 TempoMap::do_insert (MetricSection* section)
922 bool need_add = true;
923 /* we only allow new meters to be inserted on beat 1 of an existing
927 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
929 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
931 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
932 corrected.second.beats = 1;
933 corrected.second.ticks = 0;
934 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
935 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
936 m->bbt(), corrected.second) << endmsg;
937 //m->set_pulse (corrected);
941 /* Look for any existing MetricSection that is of the same type and
942 in the same bar as the new one, and remove it before adding
943 the new one. Note that this means that if we find a matching,
944 existing section, we can break out of the loop since we're
945 guaranteed that there is only one such match.
948 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
950 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
951 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
952 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
953 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
955 if (tempo && insert_tempo) {
958 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
959 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
961 if (tempo->initial()) {
963 /* can't (re)move this section, so overwrite
964 * its data content (but not its properties as
968 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
969 (*i)->set_position_lock_style (AudioTime);
971 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
972 t->set_type (insert_tempo->type());
982 } else if (meter && insert_meter) {
986 bool const ipm = insert_meter->position_lock_style() == MusicTime;
988 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
990 if (meter->initial()) {
992 /* can't (re)move this section, so overwrite
993 * its data content (but not its properties as
997 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
998 (*i)->set_position_lock_style (AudioTime);
1008 /* non-matching types, so we don't care */
1012 /* Add the given MetricSection, if we didn't just reset an existing
1017 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
1018 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
1019 Metrics::iterator i;
1022 TempoSection* prev_t = 0;
1024 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1025 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1026 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1029 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1033 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1037 prev_t = dynamic_cast<TempoSection*> (*i);
1040 } else if (insert_tempo) {
1041 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1042 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1045 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1046 const bool lm = insert_tempo->locked_to_meter();
1047 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1048 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1055 _metrics.insert (i, section);
1059 /* user supplies the exact pulse if pls == MusicTime */
1061 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
1063 if (tempo.note_types_per_minute() <= 0.0) {
1064 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1068 TempoSection* ts = 0;
1070 Glib::Threads::RWLock::WriterLock lm (lock);
1071 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
1075 PropertyChanged (PropertyChange ());
1081 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
1083 if (tempo.note_types_per_minute() <= 0.0) {
1084 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1088 const bool locked_to_meter = ts.locked_to_meter();
1091 Glib::Threads::RWLock::WriterLock lm (lock);
1092 TempoSection& first (first_tempo());
1093 if (!ts.initial()) {
1094 if (ts.locked_to_meter()) {
1097 /* cannot move a meter-locked tempo section */
1098 *static_cast<Tempo*>(&ts) = tempo;
1099 recompute_map (_metrics);
1102 remove_tempo_locked (ts);
1103 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1106 first.set_type (type);
1107 first.set_pulse (0.0);
1108 first.set_minute (minute_at_frame (frame));
1109 first.set_position_lock_style (AudioTime);
1111 /* cannot move the first tempo section */
1112 *static_cast<Tempo*>(&first) = tempo;
1113 recompute_map (_metrics);
1118 PropertyChanged (PropertyChange ());
1122 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1123 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1125 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1126 t->set_locked_to_meter (locked_to_meter);
1127 bool solved = false;
1132 if (pls == AudioTime) {
1133 solved = solve_map_minute (_metrics, t, t->minute());
1135 solved = solve_map_pulse (_metrics, t, t->pulse());
1137 recompute_meters (_metrics);
1140 if (!solved && recompute) {
1141 recompute_map (_metrics);
1148 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1150 MeterSection* m = 0;
1152 Glib::Threads::RWLock::WriterLock lm (lock);
1153 m = add_meter_locked (meter, beat, where, frame, pls, true);
1158 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1163 PropertyChanged (PropertyChange ());
1168 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1171 Glib::Threads::RWLock::WriterLock lm (lock);
1172 const double beat = beat_at_bbt_locked (_metrics, where);
1174 if (!ms.initial()) {
1175 remove_meter_locked (ms);
1176 add_meter_locked (meter, beat, where, frame, pls, true);
1178 MeterSection& first (first_meter());
1179 TempoSection& first_t (first_tempo());
1180 /* cannot move the first meter section */
1181 *static_cast<Meter*>(&first) = meter;
1182 first.set_position_lock_style (AudioTime);
1183 first.set_pulse (0.0);
1184 first.set_minute (minute_at_frame (frame));
1185 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1186 first.set_beat (beat);
1187 first_t.set_minute (first.minute());
1188 first_t.set_pulse (0.0);
1189 first_t.set_position_lock_style (AudioTime);
1190 recompute_map (_metrics);
1194 PropertyChanged (PropertyChange ());
1198 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1200 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1201 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1202 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1203 TempoSection* mlt = 0;
1205 if (pls == AudioTime) {
1206 /* add meter-locked tempo */
1207 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), TempoSection::Ramp, AudioTime, true, true);
1215 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1217 bool solved = false;
1219 do_insert (new_meter);
1223 if (pls == AudioTime) {
1224 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1225 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1226 fudge frame so that the meter ends up at its BBT position instead.
1229 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1232 solved = solve_map_bbt (_metrics, new_meter, where);
1233 /* required due to resetting the pulse of meter-locked tempi above.
1234 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1235 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1237 recompute_map (_metrics);
1241 if (!solved && recompute) {
1242 /* if this has failed to solve, there is little we can do other than to ensure that
1243 the new map is recalculated.
1245 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1246 recompute_map (_metrics);
1253 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1255 Tempo newtempo (note_types_per_minute, note_type);
1258 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1259 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1264 Glib::Threads::RWLock::WriterLock lm (lock);
1265 *((Tempo*) t) = newtempo;
1266 recompute_map (_metrics);
1268 PropertyChanged (PropertyChange ());
1275 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1277 Tempo newtempo (note_types_per_minute, note_type);
1280 TempoSection* first;
1281 Metrics::iterator i;
1283 /* find the TempoSection immediately preceding "where"
1286 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1288 if ((*i)->frame() > where) {
1294 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1307 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1317 Glib::Threads::RWLock::WriterLock lm (lock);
1318 /* cannot move the first tempo section */
1319 *((Tempo*)prev) = newtempo;
1320 recompute_map (_metrics);
1323 PropertyChanged (PropertyChange ());
1327 TempoMap::first_meter () const
1329 const MeterSection *m = 0;
1331 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1332 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1337 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1338 abort(); /*NOTREACHED*/
1343 TempoMap::first_meter ()
1345 MeterSection *m = 0;
1347 /* CALLER MUST HOLD LOCK */
1349 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1350 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1355 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1356 abort(); /*NOTREACHED*/
1361 TempoMap::first_tempo () const
1363 const TempoSection *t = 0;
1365 /* CALLER MUST HOLD LOCK */
1367 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1368 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1378 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1379 abort(); /*NOTREACHED*/
1384 TempoMap::first_tempo ()
1386 TempoSection *t = 0;
1388 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1389 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1399 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1400 abort(); /*NOTREACHED*/
1404 TempoMap::recompute_tempi (Metrics& metrics)
1406 TempoSection* prev_t = 0;
1408 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1411 if ((*i)->is_tempo()) {
1412 t = static_cast<TempoSection*> (*i);
1424 if (t->position_lock_style() == AudioTime) {
1425 prev_t->set_c (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
1426 if (!t->locked_to_meter()) {
1427 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1431 prev_t->set_c (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
1432 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1440 prev_t->set_c (0.0);
1443 /* tempos must be positioned correctly.
1444 the current approach is to use a meter's bbt time as its base position unit.
1445 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1446 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1449 TempoMap::recompute_meters (Metrics& metrics)
1451 MeterSection* meter = 0;
1452 MeterSection* prev_m = 0;
1454 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1455 if (!(*mi)->is_tempo()) {
1456 meter = static_cast<MeterSection*> (*mi);
1457 if (meter->position_lock_style() == AudioTime) {
1459 pair<double, BBT_Time> b_bbt;
1460 TempoSection* meter_locked_tempo = 0;
1461 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1463 if ((*ii)->is_tempo()) {
1464 t = static_cast<TempoSection*> (*ii);
1465 if ((t->locked_to_meter() || t->initial()) && t->frame() == meter->frame()) {
1466 meter_locked_tempo = t;
1473 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1474 if (beats + prev_m->beat() != meter->beat()) {
1475 /* reordering caused a bbt change */
1477 beats = meter->beat() - prev_m->beat();
1478 b_bbt = make_pair (beats + prev_m->beat()
1479 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1480 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1482 } else if (!meter->initial()) {
1483 b_bbt = make_pair (meter->beat(), meter->bbt());
1484 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1487 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1489 if (meter_locked_tempo) {
1490 meter_locked_tempo->set_pulse (pulse);
1492 meter->set_beat (b_bbt);
1493 meter->set_pulse (pulse);
1498 pair<double, BBT_Time> b_bbt;
1500 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1501 if (beats + prev_m->beat() != meter->beat()) {
1502 /* reordering caused a bbt change */
1503 b_bbt = make_pair (beats + prev_m->beat()
1504 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1506 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1508 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1510 /* shouldn't happen - the first is audio-locked */
1511 pulse = pulse_at_beat_locked (metrics, meter->beat());
1512 b_bbt = make_pair (meter->beat(), meter->bbt());
1515 meter->set_beat (b_bbt);
1516 meter->set_pulse (pulse);
1517 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1526 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1528 /* CALLER MUST HOLD WRITE LOCK */
1530 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1533 /* silly call from Session::process() during startup
1538 recompute_tempi (metrics);
1539 recompute_meters (metrics);
1543 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1545 Glib::Threads::RWLock::ReaderLock lm (lock);
1546 TempoMetric m (first_meter(), first_tempo());
1548 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1549 at something, because we insert the default tempo and meter during
1550 TempoMap construction.
1552 now see if we can find better candidates.
1555 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1557 if ((*i)->frame() > frame) {
1571 /* XX meters only */
1573 TempoMap::metric_at (BBT_Time bbt) const
1575 Glib::Threads::RWLock::ReaderLock lm (lock);
1576 TempoMetric m (first_meter(), first_tempo());
1578 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1579 at something, because we insert the default tempo and meter during
1580 TempoMap construction.
1582 now see if we can find better candidates.
1585 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1587 if (!(*i)->is_tempo()) {
1588 mw = static_cast<MeterSection*> (*i);
1589 BBT_Time section_start (mw->bbt());
1591 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1602 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1603 * @param frame The session frame position.
1604 * @return The beat duration according to the tempo map at the supplied frame.
1606 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1607 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1609 * This function uses both tempo and meter.
1612 TempoMap::beat_at_frame (const framecnt_t& frame) const
1614 Glib::Threads::RWLock::ReaderLock lm (lock);
1616 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1619 /* This function uses both tempo and meter.*/
1621 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1623 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1624 MeterSection* prev_m = 0;
1625 MeterSection* next_m = 0;
1627 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1628 if (!(*i)->is_tempo()) {
1629 if (prev_m && (*i)->minute() > minute) {
1630 next_m = static_cast<MeterSection*> (*i);
1633 prev_m = static_cast<MeterSection*> (*i);
1637 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1639 /* audio locked meters fake their beat */
1640 if (next_m && next_m->beat() < beat) {
1641 return next_m->beat();
1647 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1648 * @param beat The BBT (meter-based) beat.
1649 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1651 * This function uses both tempo and meter.
1654 TempoMap::frame_at_beat (const double& beat) const
1656 Glib::Threads::RWLock::ReaderLock lm (lock);
1658 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1661 /* meter & tempo section based */
1663 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1665 MeterSection* prev_m = 0;
1666 TempoSection* prev_t = 0;
1670 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1671 if (!(*i)->is_tempo()) {
1672 m = static_cast<MeterSection*> (*i);
1673 if (prev_m && m->beat() > beat) {
1683 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1684 if ((*i)->is_tempo()) {
1685 t = static_cast<TempoSection*> (*i);
1691 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1700 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1703 /** Returns a Tempo corresponding to the supplied frame position.
1704 * @param frame The audio frame.
1705 * @return a Tempo according to the tempo map at the supplied frame.
1709 TempoMap::tempo_at_frame (const framepos_t& frame) const
1711 Glib::Threads::RWLock::ReaderLock lm (lock);
1713 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1717 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1719 TempoSection* prev_t = 0;
1723 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1724 if ((*i)->is_tempo()) {
1725 t = static_cast<TempoSection*> (*i);
1729 if ((prev_t) && t->minute() > minute) {
1730 /* t is the section past frame */
1731 return prev_t->tempo_at_minute (minute);
1737 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1740 /** returns the frame at which the supplied tempo occurs, or
1741 * the frame of the last tempo section (search exhausted)
1742 * only the position of the first occurence will be returned
1746 TempoMap::frame_at_tempo (const Tempo& tempo) const
1748 Glib::Threads::RWLock::ReaderLock lm (lock);
1750 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1754 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1756 TempoSection* prev_t = 0;
1757 const double tempo_bpm = tempo.note_types_per_minute();
1759 Metrics::const_iterator i;
1761 for (i = metrics.begin(); i != metrics.end(); ++i) {
1763 if ((*i)->is_tempo()) {
1764 t = static_cast<TempoSection*> (*i);
1770 const double t_bpm = t->note_types_per_minute();
1772 if (t_bpm == tempo_bpm) {
1777 const double prev_t_bpm = prev_t->note_types_per_minute();
1779 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1780 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1787 return prev_t->minute();
1791 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1793 TempoSection* prev_t = 0;
1797 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1798 if ((*i)->is_tempo()) {
1799 t = static_cast<TempoSection*> (*i);
1803 if ((prev_t) && t->pulse() > pulse) {
1804 /* t is the section past frame */
1805 return prev_t->tempo_at_pulse (pulse);
1811 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1815 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1817 TempoSection* prev_t = 0;
1818 const double tempo_bpm = tempo.note_types_per_minute();
1820 Metrics::const_iterator i;
1822 for (i = metrics.begin(); i != metrics.end(); ++i) {
1824 if ((*i)->is_tempo()) {
1825 t = static_cast<TempoSection*> (*i);
1831 const double t_bpm = t->note_types_per_minute();
1833 if (t_bpm == tempo_bpm) {
1838 const double prev_t_bpm = prev_t->note_types_per_minute();
1840 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1841 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1848 return prev_t->pulse();
1851 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1852 * @param qn the position in quarter note beats.
1853 * @return the Tempo at the supplied quarter-note.
1856 TempoMap::tempo_at_quarter_note (const double& qn) const
1858 Glib::Threads::RWLock::ReaderLock lm (lock);
1860 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1863 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1864 * @param tempo the tempo.
1865 * @return the position in quarter-note beats where the map bpm
1866 * is equal to that of the Tempo. currently ignores note_type.
1869 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1871 Glib::Threads::RWLock::ReaderLock lm (lock);
1873 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1876 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1877 * @param metrics the list of metric sections used to calculate the pulse.
1878 * @param beat The BBT (meter-based) beat.
1879 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1881 * a pulse or whole note is the base musical position of a MetricSection.
1882 * it is equivalent to four quarter notes.
1886 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1888 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1890 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1893 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1894 * @param metrics the list of metric sections used to calculate the beat.
1895 * @param pulse the whole-note pulse.
1896 * @return the meter-based beat at the supplied whole-note pulse.
1898 * a pulse or whole note is the base musical position of a MetricSection.
1899 * it is equivalent to four quarter notes.
1902 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1904 MeterSection* prev_m = 0;
1906 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1908 if (!(*i)->is_tempo()) {
1909 m = static_cast<MeterSection*> (*i);
1910 if (prev_m && m->pulse() > pulse) {
1918 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1922 /* tempo section based */
1924 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1926 /* HOLD (at least) THE READER LOCK */
1927 TempoSection* prev_t = 0;
1929 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1931 if ((*i)->is_tempo()) {
1932 t = static_cast<TempoSection*> (*i);
1936 if (prev_t && t->minute() > minute) {
1937 /*the previous ts is the one containing the frame */
1938 const double ret = prev_t->pulse_at_minute (minute);
1939 /* audio locked section in new meter*/
1940 if (t->pulse() < ret) {
1949 /* treated as constant for this ts */
1950 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1952 return pulses_in_section + prev_t->pulse();
1955 /* tempo section based */
1957 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1959 /* HOLD THE READER LOCK */
1961 const TempoSection* prev_t = 0;
1963 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1966 if ((*i)->is_tempo()) {
1967 t = static_cast<TempoSection*> (*i);
1971 if (prev_t && t->pulse() > pulse) {
1972 return prev_t->minute_at_pulse (pulse);
1978 /* must be treated as constant, irrespective of _type */
1979 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1981 return dtime + prev_t->minute();
1984 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1985 * @param bbt The BBT time (meter-based).
1986 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1990 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1992 Glib::Threads::RWLock::ReaderLock lm (lock);
1993 return beat_at_bbt_locked (_metrics, bbt);
1998 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2000 /* CALLER HOLDS READ LOCK */
2002 MeterSection* prev_m = 0;
2004 /* because audio-locked meters have 'fake' integral beats,
2005 there is no pulse offset here.
2009 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2010 if (!(*i)->is_tempo()) {
2011 m = static_cast<MeterSection*> (*i);
2013 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2014 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2022 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2023 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2024 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2029 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2030 * @param beat The BBT (meter-based) beat.
2031 * @return The BBT time (meter-based) at the supplied meter-based beat.
2035 TempoMap::bbt_at_beat (const double& beat)
2037 Glib::Threads::RWLock::ReaderLock lm (lock);
2038 return bbt_at_beat_locked (_metrics, beat);
2042 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2044 /* CALLER HOLDS READ LOCK */
2045 MeterSection* prev_m = 0;
2046 const double beats = max (0.0, b);
2048 MeterSection* m = 0;
2050 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2051 if (!(*i)->is_tempo()) {
2052 m = static_cast<MeterSection*> (*i);
2054 if (m->beat() > beats) {
2055 /* this is the meter after the one our beat is on*/
2065 const double beats_in_ms = beats - prev_m->beat();
2066 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2067 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2068 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2069 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2073 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2074 ret.beats = (uint32_t) floor (remaining_beats);
2075 ret.bars = total_bars;
2077 /* 0 0 0 to 1 1 0 - based mapping*/
2081 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2083 ret.ticks -= BBT_Time::ticks_per_beat;
2086 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2094 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2095 * @param bbt The BBT time (meter-based).
2096 * @return the quarter note beat at the supplied BBT time
2098 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2100 * while the input uses meter, the output does not.
2103 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2105 Glib::Threads::RWLock::ReaderLock lm (lock);
2107 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2111 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2113 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2116 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2119 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2123 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2125 /* CALLER HOLDS READ LOCK */
2127 MeterSection* prev_m = 0;
2129 /* because audio-locked meters have 'fake' integral beats,
2130 there is no pulse offset here.
2134 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2135 if (!(*i)->is_tempo()) {
2136 m = static_cast<MeterSection*> (*i);
2138 if (m->bbt().bars > bbt.bars) {
2146 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2147 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2148 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2153 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2154 * @param qn the quarter-note beat.
2155 * @return The BBT time (meter-based) at the supplied meter-based beat.
2157 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2161 TempoMap::bbt_at_quarter_note (const double& qn)
2163 Glib::Threads::RWLock::ReaderLock lm (lock);
2165 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2168 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2169 * @param metrics The list of metric sections used to determine the result.
2170 * @param pulse The whole-note pulse.
2171 * @return The BBT time at the supplied whole-note pulse.
2173 * a pulse or whole note is the basic musical position of a MetricSection.
2174 * it is equivalent to four quarter notes.
2175 * while the output uses meter, the input does not.
2178 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2180 MeterSection* prev_m = 0;
2182 MeterSection* m = 0;
2184 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2186 if (!(*i)->is_tempo()) {
2187 m = static_cast<MeterSection*> (*i);
2190 double const pulses_to_m = m->pulse() - prev_m->pulse();
2191 if (prev_m->pulse() + pulses_to_m > pulse) {
2192 /* this is the meter after the one our beat is on*/
2203 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2204 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2205 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2206 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2207 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2211 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2212 ret.beats = (uint32_t) floor (remaining_beats);
2213 ret.bars = total_bars;
2215 /* 0 0 0 to 1 1 0 mapping*/
2219 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2221 ret.ticks -= BBT_Time::ticks_per_beat;
2224 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2232 /** Returns the BBT time corresponding to the supplied frame position.
2233 * @param frame the position in audio samples.
2234 * @return the BBT time at the frame position .
2238 TempoMap::bbt_at_frame (framepos_t frame)
2245 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2248 Glib::Threads::RWLock::ReaderLock lm (lock);
2250 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2254 TempoMap::bbt_at_frame_rt (framepos_t frame)
2256 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2259 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2262 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2266 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2276 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2277 MeterSection* prev_m = 0;
2278 MeterSection* next_m = 0;
2282 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2283 if (!(*i)->is_tempo()) {
2284 m = static_cast<MeterSection*> (*i);
2285 if (prev_m && m->minute() > minute) {
2293 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2295 /* handle frame before first meter */
2296 if (minute < prev_m->minute()) {
2299 /* audio locked meters fake their beat */
2300 if (next_m && next_m->beat() < beat) {
2301 beat = next_m->beat();
2304 beat = max (0.0, beat);
2306 const double beats_in_ms = beat - prev_m->beat();
2307 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2308 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2309 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2310 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2314 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2315 ret.beats = (uint32_t) floor (remaining_beats);
2316 ret.bars = total_bars;
2318 /* 0 0 0 to 1 1 0 - based mapping*/
2322 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2324 ret.ticks -= BBT_Time::ticks_per_beat;
2327 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2335 /** Returns the frame position corresponding to the supplied BBT time.
2336 * @param bbt the position in BBT time.
2337 * @return the frame position at bbt.
2341 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2344 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2348 if (bbt.beats < 1) {
2349 throw std::logic_error ("beats are counted from one");
2351 Glib::Threads::RWLock::ReaderLock lm (lock);
2353 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2356 /* meter & tempo section based */
2358 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2360 /* HOLD THE READER LOCK */
2362 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2367 * Returns the quarter-note beat position corresponding to the supplied frame.
2369 * @param frame the position in frames.
2370 * @return The quarter-note position of the supplied frame. Ignores meter.
2374 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2376 Glib::Threads::RWLock::ReaderLock lm (lock);
2378 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2384 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2386 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2392 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2394 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2397 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2400 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2406 * Returns the frame position corresponding to the supplied quarter-note beat.
2408 * @param quarter_note the quarter-note position.
2409 * @return the frame position of the supplied quarter-note. Ignores meter.
2414 TempoMap::frame_at_quarter_note (const double quarter_note) const
2416 Glib::Threads::RWLock::ReaderLock lm (lock);
2418 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2424 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2426 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2431 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2432 * @param beat The BBT (meter-based) beat.
2433 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2435 * a quarter-note may be compared with and assigned to Evoral::Beats.
2439 TempoMap::quarter_note_at_beat (const double beat) const
2441 Glib::Threads::RWLock::ReaderLock lm (lock);
2443 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2449 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2451 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2456 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2457 * @param quarter_note The position in quarter-note beats.
2458 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2460 * a quarter-note is the musical unit of Evoral::Beats.
2464 TempoMap::beat_at_quarter_note (const double quarter_note) const
2466 Glib::Threads::RWLock::ReaderLock lm (lock);
2468 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2474 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2477 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2480 /** Returns the duration in frames between two supplied quarter-note beat positions.
2481 * @param start the first position in quarter-note beats.
2482 * @param end the end position in quarter-note beats.
2483 * @return the frame distance ober the quarter-note beats duration.
2485 * use this rather than e.g.
2486 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2487 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2491 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2493 Glib::Threads::RWLock::ReaderLock lm (lock);
2495 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2499 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2502 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2506 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2508 Glib::Threads::RWLock::ReaderLock lm (lock);
2510 return quarter_notes_between_frames_locked (_metrics, start, end);
2514 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2516 const TempoSection* prev_t = 0;
2518 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2521 if ((*i)->is_tempo()) {
2522 t = static_cast<TempoSection*> (*i);
2526 if (prev_t && t->frame() > start) {
2533 const double start_qn = prev_t->pulse_at_frame (start);
2535 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2538 if ((*i)->is_tempo()) {
2539 t = static_cast<TempoSection*> (*i);
2543 if (prev_t && t->frame() > end) {
2549 const double end_qn = prev_t->pulse_at_frame (end);
2551 return (end_qn - start_qn) * 4.0;
2555 TempoMap::check_solved (const Metrics& metrics) const
2557 TempoSection* prev_t = 0;
2558 MeterSection* prev_m = 0;
2560 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2563 if ((*i)->is_tempo()) {
2564 t = static_cast<TempoSection*> (*i);
2569 /* check ordering */
2570 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2574 /* precision check ensures tempo and frames align.*/
2575 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2576 if (!t->locked_to_meter()) {
2581 /* gradient limit - who knows what it should be?
2582 things are also ok (if a little chaotic) without this
2584 if (fabs (prev_t->c()) > 1000.0) {
2585 //std::cout << "c : " << prev_t->c() << std::endl;
2592 if (!(*i)->is_tempo()) {
2593 m = static_cast<MeterSection*> (*i);
2594 if (prev_m && m->position_lock_style() == AudioTime) {
2595 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2596 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2597 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2599 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2613 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2615 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2617 if ((*i)->is_tempo()) {
2618 t = static_cast<TempoSection*> (*i);
2619 if (t->locked_to_meter()) {
2620 t->set_active (true);
2621 } else if (t->position_lock_style() == AudioTime) {
2622 if (t->frame() < frame) {
2623 t->set_active (false);
2624 t->set_pulse (-1.0);
2625 } else if (t->frame() > frame) {
2626 t->set_active (true);
2627 } else if (t->frame() == frame) {
2637 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2639 TempoSection* prev_t = 0;
2640 TempoSection* section_prev = 0;
2641 double first_m_minute = 0.0;
2642 const bool sml = section->locked_to_meter();
2644 /* can't move a tempo before the first meter */
2645 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2647 if (!(*i)->is_tempo()) {
2648 m = static_cast<MeterSection*> (*i);
2650 first_m_minute = m->minute();
2655 if (!section->initial() && minute <= first_m_minute) {
2659 section->set_active (true);
2660 section->set_minute (minute);
2662 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2664 if ((*i)->is_tempo()) {
2665 t = static_cast<TempoSection*> (*i);
2677 if (t->frame() == frame_at_minute (minute)) {
2681 const bool tlm = t->position_lock_style() == MusicTime;
2683 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2684 section_prev = prev_t;
2686 section_prev->set_c (section_prev->compute_c_func_minute (section->note_types_per_minute(), minute));
2687 if (!section->locked_to_meter()) {
2688 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2693 if (t->position_lock_style() == MusicTime) {
2694 prev_t->set_c (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2695 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2697 prev_t->set_c (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2698 if (!t->locked_to_meter()) {
2699 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2708 recompute_tempi (imaginary);
2710 if (check_solved (imaginary)) {
2713 dunp (imaginary, std::cout);
2717 MetricSectionFrameSorter fcmp;
2718 imaginary.sort (fcmp);
2720 recompute_tempi (imaginary);
2722 if (check_solved (imaginary)) {
2730 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2732 TempoSection* prev_t = 0;
2733 TempoSection* section_prev = 0;
2735 section->set_pulse (pulse);
2737 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2739 if ((*i)->is_tempo()) {
2740 t = static_cast<TempoSection*> (*i);
2751 section_prev = prev_t;
2755 if (t->position_lock_style() == MusicTime) {
2756 prev_t->set_c (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2757 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2759 prev_t->set_c (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2760 if (!t->locked_to_meter()) {
2761 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2770 section_prev->set_c (section_prev->compute_c_func_pulse (section->note_types_per_minute(), pulse));
2771 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2775 recompute_tempi (imaginary);
2777 if (check_solved (imaginary)) {
2780 dunp (imaginary, std::cout);
2784 MetricSectionSorter cmp;
2785 imaginary.sort (cmp);
2787 recompute_tempi (imaginary);
2789 * XX need a restriction here, but only for this case,
2790 * as audio locked tempos don't interact in the same way.
2792 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2794 * |50 bpm |250 bpm |60 bpm
2795 * drag 250 to the pulse after 60->
2796 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2798 if (check_solved (imaginary)) {
2806 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2808 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2809 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2810 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2814 if (section->initial()) {
2815 /* lock the first tempo to our first meter */
2816 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2821 TempoSection* meter_locked_tempo = 0;
2823 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2825 if ((*ii)->is_tempo()) {
2826 t = static_cast<TempoSection*> (*ii);
2827 if ((t->locked_to_meter() || t->initial()) && t->frame() == section->frame()) {
2828 meter_locked_tempo = t;
2834 if (!meter_locked_tempo) {
2838 MeterSection* prev_m = 0;
2840 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2841 bool solved = false;
2843 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2845 if (!(*i)->is_tempo()) {
2846 m = static_cast<MeterSection*> (*i);
2848 if (prev_m && !section->initial()) {
2849 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2850 if (beats + prev_m->beat() < section->beat()) {
2851 /* set the section pulse according to its musical position,
2852 * as an earlier time than this has been requested.
2854 const double new_pulse = ((section->beat() - prev_m->beat())
2855 / prev_m->note_divisor()) + prev_m->pulse();
2857 tempo_copy->set_position_lock_style (MusicTime);
2858 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2859 meter_locked_tempo->set_position_lock_style (MusicTime);
2860 section->set_position_lock_style (MusicTime);
2861 section->set_pulse (new_pulse);
2862 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2863 meter_locked_tempo->set_position_lock_style (AudioTime);
2864 section->set_position_lock_style (AudioTime);
2865 section->set_minute (meter_locked_tempo->minute());
2871 Metrics::const_iterator d = future_map.begin();
2872 while (d != future_map.end()) {
2881 /* all is ok. set section's locked tempo if allowed.
2882 possibly disallow if there is an adjacent audio-locked tempo.
2883 XX this check could possibly go. its never actually happened here.
2885 MeterSection* meter_copy = const_cast<MeterSection*>
2886 (&meter_section_at_minute_locked (future_map, section->minute()));
2888 meter_copy->set_minute (minute);
2890 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2891 section->set_minute (minute);
2892 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2893 / prev_m->note_divisor()) + prev_m->pulse());
2894 solve_map_minute (imaginary, meter_locked_tempo, minute);
2899 Metrics::const_iterator d = future_map.begin();
2900 while (d != future_map.end()) {
2910 /* initial (first meter atm) */
2912 tempo_copy->set_minute (minute);
2913 tempo_copy->set_pulse (0.0);
2915 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2916 section->set_minute (minute);
2917 meter_locked_tempo->set_minute (minute);
2918 meter_locked_tempo->set_pulse (0.0);
2919 solve_map_minute (imaginary, meter_locked_tempo, minute);
2924 Metrics::const_iterator d = future_map.begin();
2925 while (d != future_map.end()) {
2934 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2935 section->set_beat (b_bbt);
2936 section->set_pulse (0.0);
2946 MetricSectionFrameSorter fcmp;
2947 imaginary.sort (fcmp);
2949 recompute_meters (imaginary);
2955 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2957 /* disallow setting section to an existing meter's bbt */
2958 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2960 if (!(*i)->is_tempo()) {
2961 m = static_cast<MeterSection*> (*i);
2962 if (m != section && m->bbt().bars == when.bars) {
2968 MeterSection* prev_m = 0;
2969 MeterSection* section_prev = 0;
2971 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2973 if (!(*i)->is_tempo()) {
2974 m = static_cast<MeterSection*> (*i);
2980 pair<double, BBT_Time> b_bbt;
2981 double new_pulse = 0.0;
2983 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2984 section_prev = prev_m;
2986 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2987 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2988 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2990 section->set_beat (b_bbt);
2991 section->set_pulse (pulse);
2992 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2996 if (m->position_lock_style() == AudioTime) {
2997 TempoSection* meter_locked_tempo = 0;
2999 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
3001 if ((*ii)->is_tempo()) {
3002 t = static_cast<TempoSection*> (*ii);
3003 if ((t->locked_to_meter() || t->initial()) && t->frame() == m->frame()) {
3004 meter_locked_tempo = t;
3010 if (!meter_locked_tempo) {
3015 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3017 if (beats + prev_m->beat() != m->beat()) {
3018 /* tempo/ meter change caused a change in beat (bar). */
3020 /* the user has requested that the previous section of music overlaps this one.
3021 we have no choice but to change the bar number here, as being locked to audio means
3022 we must stay where we are on the timeline.
3024 beats = m->beat() - prev_m->beat();
3025 b_bbt = make_pair (beats + prev_m->beat()
3026 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3027 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3029 } else if (!m->initial()) {
3030 b_bbt = make_pair (m->beat(), m->bbt());
3031 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3034 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3037 meter_locked_tempo->set_pulse (new_pulse);
3038 m->set_beat (b_bbt);
3039 m->set_pulse (new_pulse);
3043 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3044 if (beats + prev_m->beat() != m->beat()) {
3045 /* tempo/ meter change caused a change in beat (bar). */
3046 b_bbt = make_pair (beats + prev_m->beat()
3047 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3049 b_bbt = make_pair (beats + prev_m->beat()
3052 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3053 m->set_beat (b_bbt);
3054 m->set_pulse (new_pulse);
3055 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3062 if (!section_prev) {
3064 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3065 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3066 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3068 section->set_beat (b_bbt);
3069 section->set_pulse (pulse);
3070 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3073 MetricSectionSorter cmp;
3074 imaginary.sort (cmp);
3076 recompute_meters (imaginary);
3081 /** places a copy of _metrics into copy and returns a pointer
3082 * to section's equivalent in copy.
3085 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3087 TempoSection* ret = 0;
3089 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3092 if ((*i)->is_tempo()) {
3093 t = static_cast<TempoSection*> (*i);
3095 ret = new TempoSection (*t);
3096 copy.push_back (ret);
3100 TempoSection* cp = new TempoSection (*t);
3101 copy.push_back (cp);
3103 if (!(*i)->is_tempo()) {
3104 m = static_cast<MeterSection *> (*i);
3105 MeterSection* cp = new MeterSection (*m);
3106 copy.push_back (cp);
3114 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3116 MeterSection* ret = 0;
3118 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3121 if ((*i)->is_tempo()) {
3122 t = static_cast<TempoSection*> (*i);
3123 TempoSection* cp = new TempoSection (*t);
3124 copy.push_back (cp);
3127 if (!(*i)->is_tempo()) {
3128 m = static_cast<MeterSection *> (*i);
3130 ret = new MeterSection (*m);
3131 copy.push_back (ret);
3134 MeterSection* cp = new MeterSection (*m);
3135 copy.push_back (cp);
3142 /** answers the question "is this a valid beat position for this tempo section?".
3143 * it returns true if the tempo section can be moved to the requested bbt position,
3144 * leaving the tempo map in a solved state.
3145 * @param ts the tempo section to be moved
3146 * @param bbt the requested new position for the tempo section
3147 * @return true if the tempo section can be moved to the position, otherwise false.
3150 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3153 TempoSection* tempo_copy = 0;
3156 Glib::Threads::RWLock::ReaderLock lm (lock);
3157 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3163 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3165 Metrics::const_iterator d = copy.begin();
3166 while (d != copy.end()) {
3175 * 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,
3176 * taking any possible reordering as a consequence of this into account.
3177 * @param section - the section to be altered
3178 * @param bbt - the BBT time where the altered tempo will fall
3179 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3181 pair<double, framepos_t>
3182 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3185 pair<double, framepos_t> ret = make_pair (0.0, 0);
3187 Glib::Threads::RWLock::ReaderLock lm (lock);
3189 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3191 const double beat = beat_at_bbt_locked (future_map, bbt);
3193 if (section->position_lock_style() == AudioTime) {
3194 tempo_copy->set_position_lock_style (MusicTime);
3197 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3198 ret.first = tempo_copy->pulse();
3199 ret.second = tempo_copy->frame();
3201 ret.first = section->pulse();
3202 ret.second = section->frame();
3205 Metrics::const_iterator d = future_map.begin();
3206 while (d != future_map.end()) {
3213 /** moves a TempoSection to a specified position.
3214 * @param ts - the section to be moved
3215 * @param frame - the new position in frames for the tempo
3216 * @param sub_num - the snap division to use if using musical time.
3218 * if sub_num is non-zero, the frame position is used to calculate an exact
3221 * -1 | snap to bars (meter-based)
3222 * 0 | no snap - use audio frame for musical position
3223 * 1 | snap to meter-based (BBT) beat
3224 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3226 * this follows the snap convention in the gui.
3227 * if sub_num is zero, the musical position will be taken from the supplied frame.
3230 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3234 if (ts->position_lock_style() == MusicTime) {
3236 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3237 Glib::Threads::RWLock::WriterLock lm (lock);
3238 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3240 tempo_copy->set_position_lock_style (AudioTime);
3242 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3243 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3244 const double pulse = pulse_at_beat_locked (future_map, beat);
3246 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3247 solve_map_pulse (_metrics, ts, pulse);
3248 recompute_meters (_metrics);
3256 Glib::Threads::RWLock::WriterLock lm (lock);
3257 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3259 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3261 /* We're moving the object that defines the grid while snapping to it...
3262 * Placing the ts at the beat corresponding to the requested frame may shift the
3263 * grid in such a way that the mouse is left hovering over a completerly different division,
3264 * causing jittering when the mouse next moves (esp. large tempo deltas).
3266 * This alters the snap behaviour slightly in that we snap to beat divisions
3267 * in the future map rather than the existing one.
3269 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3270 const framepos_t snapped_frame = frame_at_minute (minute_at_quarter_note_locked (future_map, qn));
3272 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3273 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3274 ts->set_pulse (qn / 4.0);
3275 recompute_meters (_metrics);
3278 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3279 recompute_meters (_metrics);
3285 Metrics::const_iterator d = future_map.begin();
3286 while (d != future_map.end()) {
3291 MetricPositionChanged (); // Emit Signal
3294 /** moves a MeterSection to a specified position.
3295 * @param ms - the section to be moved
3296 * @param frame - the new position in frames for the meter
3298 * as a meter cannot snap to anything but bars,
3299 * the supplied frame is rounded to the nearest bar, possibly
3300 * leaving the meter position unchanged.
3303 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3307 if (ms->position_lock_style() == AudioTime) {
3310 Glib::Threads::RWLock::WriterLock lm (lock);
3311 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3313 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3314 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3315 recompute_tempi (_metrics);
3320 Glib::Threads::RWLock::WriterLock lm (lock);
3321 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3323 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3324 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3326 if (solve_map_bbt (future_map, copy, bbt)) {
3327 solve_map_bbt (_metrics, ms, bbt);
3328 recompute_tempi (_metrics);
3333 Metrics::const_iterator d = future_map.begin();
3334 while (d != future_map.end()) {
3339 MetricPositionChanged (); // Emit Signal
3343 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3346 bool can_solve = false;
3348 Glib::Threads::RWLock::WriterLock lm (lock);
3349 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3350 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3351 recompute_tempi (future_map);
3353 if (check_solved (future_map)) {
3354 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3355 recompute_map (_metrics);
3360 Metrics::const_iterator d = future_map.begin();
3361 while (d != future_map.end()) {
3366 MetricPositionChanged (); // Emit Signal
3372 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3375 Ts (future prev_t) Tnext
3378 |----------|----------
3385 Glib::Threads::RWLock::WriterLock lm (lock);
3391 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3392 TempoSection* prev_to_prev_t = 0;
3393 const frameoffset_t fr_off = end_frame - frame;
3397 if (prev_t->pulse() > 0.0) {
3398 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3401 TempoSection* next_t = 0;
3402 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3403 TempoSection* t = 0;
3404 if ((*i)->is_tempo()) {
3405 t = static_cast<TempoSection*> (*i);
3406 if (t->frame() > ts->frame()) {
3412 /* minimum allowed measurement distance in frames */
3413 const framepos_t min_dframe = 2;
3415 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3416 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3418 double contribution = 0.0;
3420 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3421 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3424 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3426 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3427 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3431 if (prev_t->type() == TempoSection::Constant || prev_t->c() == 0.0) {
3433 if (prev_t->position_lock_style() == MusicTime) {
3434 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3435 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3437 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3438 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3440 new_bpm = prev_t->note_types_per_minute();
3443 /* prev to prev is irrelevant */
3445 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3446 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3448 new_bpm = prev_t->note_types_per_minute();
3453 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3454 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3456 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3457 / (double) ((end_frame) - prev_to_prev_t->frame()));
3459 new_bpm = prev_t->note_types_per_minute();
3462 /* prev_to_prev_t is irrelevant */
3464 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3465 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3467 new_bpm = prev_t->note_types_per_minute();
3473 double frame_ratio = 1.0;
3474 double pulse_ratio = 1.0;
3475 const double pulse_pos = frame;
3477 if (prev_to_prev_t) {
3478 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3479 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3481 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3482 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3485 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3486 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3488 pulse_ratio = (start_pulse / end_pulse);
3490 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3493 /* don't clamp and proceed here.
3494 testing has revealed that this can go negative,
3495 which is an entirely different thing to just being too low.
3497 if (new_bpm < 0.5) {
3500 new_bpm = min (new_bpm, (double) 1000.0);
3501 prev_t->set_note_types_per_minute (new_bpm);
3502 recompute_tempi (future_map);
3503 recompute_meters (future_map);
3505 if (check_solved (future_map)) {
3506 ts->set_note_types_per_minute (new_bpm);
3507 recompute_tempi (_metrics);
3508 recompute_meters (_metrics);
3512 Metrics::const_iterator d = future_map.begin();
3513 while (d != future_map.end()) {
3518 MetricPositionChanged (); // Emit Signal
3521 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3522 * the supplied frame, possibly returning a negative value.
3524 * @param frame The session frame position.
3525 * @param sub_num The subdivision to use when rounding the beat.
3526 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3527 * Positive integers indicate quarter note (non BBT) divisions.
3528 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3529 * @return The beat position of the supplied frame.
3531 * when working to a musical grid, the use of sub_nom indicates that
3532 * the position should be interpreted musically.
3534 * it effectively snaps to meter bars, meter beats or quarter note divisions
3535 * (as per current gui convention) and returns a musical position independent of frame rate.
3537 * If the supplied frame lies before the first meter, the return will be negative,
3538 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3539 * the continuation of the tempo curve (backwards).
3541 * This function is sensitive to tempo and meter.
3544 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3546 Glib::Threads::RWLock::ReaderLock lm (lock);
3548 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3552 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3554 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3557 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3558 * the supplied frame, possibly returning a negative value.
3560 * @param frame The session frame position.
3561 * @param sub_num The subdivision to use when rounding the quarter note.
3562 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3563 * Positive integers indicate quarter note (non BBT) divisions.
3564 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3565 * @return The quarter note position of the supplied frame.
3567 * When working to a musical grid, the use of sub_nom indicates that
3568 * the frame position should be interpreted musically.
3570 * it effectively snaps to meter bars, meter beats or quarter note divisions
3571 * (as per current gui convention) and returns a musical position independent of frame rate.
3573 * If the supplied frame lies before the first meter, the return will be negative,
3574 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3575 * the continuation of the tempo curve (backwards).
3577 * This function is tempo-sensitive.
3580 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3582 Glib::Threads::RWLock::ReaderLock lm (lock);
3584 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3588 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3590 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3593 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3594 } else if (sub_num == 1) {
3595 /* the gui requested exact musical (BBT) beat */
3596 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3597 } else if (sub_num == -1) {
3599 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3603 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3605 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3607 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3617 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3618 * @param pos the frame position in the tempo map.
3619 * @param bbt the distance in BBT time from pos to calculate.
3620 * @param dir the rounding direction..
3621 * @return the duration in frames between pos and bbt
3624 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3626 Glib::Threads::RWLock::ReaderLock lm (lock);
3628 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3630 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3633 pos_bbt.bars += bbt.bars;
3635 pos_bbt.ticks += bbt.ticks;
3636 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3638 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3641 pos_bbt.beats += bbt.beats;
3642 if ((double) pos_bbt.beats > divisions) {
3644 pos_bbt.beats -= divisions;
3646 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3648 return pos_bbt_frame - pos;
3652 if (pos_bbt.bars <= bbt.bars) {
3655 pos_bbt.bars -= bbt.bars;
3658 if (pos_bbt.ticks < bbt.ticks) {
3659 if (pos_bbt.bars > 1) {
3660 if (pos_bbt.beats == 1) {
3662 pos_bbt.beats = divisions;
3666 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3672 pos_bbt.ticks -= bbt.ticks;
3675 if (pos_bbt.beats <= bbt.beats) {
3676 if (pos_bbt.bars > 1) {
3678 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3683 pos_bbt.beats -= bbt.beats;
3686 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3693 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3695 return round_to_type (fr, dir, Bar);
3699 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3701 return round_to_type (fr, dir, Beat);
3705 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3707 Glib::Threads::RWLock::ReaderLock lm (lock);
3708 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3709 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3710 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3712 ticks -= beats * BBT_Time::ticks_per_beat;
3715 /* round to next (or same iff dir == RoundUpMaybe) */
3717 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3719 if (mod == 0 && dir == RoundUpMaybe) {
3720 /* right on the subdivision, which is fine, so do nothing */
3722 } else if (mod == 0) {
3723 /* right on the subdivision, so the difference is just the subdivision ticks */
3724 ticks += ticks_one_subdivisions_worth;
3727 /* not on subdivision, compute distance to next subdivision */
3729 ticks += ticks_one_subdivisions_worth - mod;
3732 if (ticks >= BBT_Time::ticks_per_beat) {
3733 ticks -= BBT_Time::ticks_per_beat;
3735 } else if (dir < 0) {
3737 /* round to previous (or same iff dir == RoundDownMaybe) */
3739 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3741 if (difference == 0 && dir == RoundDownAlways) {
3742 /* right on the subdivision, but force-rounding down,
3743 so the difference is just the subdivision ticks */
3744 difference = ticks_one_subdivisions_worth;
3747 if (ticks < difference) {
3748 ticks = BBT_Time::ticks_per_beat - ticks;
3750 ticks -= difference;
3754 /* round to nearest */
3757 /* compute the distance to the previous and next subdivision */
3759 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3761 /* closer to the next subdivision, so shift forward */
3763 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3765 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3767 if (ticks > BBT_Time::ticks_per_beat) {
3769 ticks -= BBT_Time::ticks_per_beat;
3770 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3773 } else if (rem > 0) {
3775 /* closer to previous subdivision, so shift backward */
3779 /* can't go backwards past zero, so ... */
3782 /* step back to previous beat */
3784 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3785 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3787 ticks = lrint (ticks - rem);
3788 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3791 /* on the subdivision, do nothing */
3795 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3801 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3803 Glib::Threads::RWLock::ReaderLock lm (lock);
3804 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3805 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3806 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3808 ticks -= beats * BBT_Time::ticks_per_beat;
3811 /* round to next (or same iff dir == RoundUpMaybe) */
3813 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3815 if (mod == 0 && dir == RoundUpMaybe) {
3816 /* right on the subdivision, which is fine, so do nothing */
3818 } else if (mod == 0) {
3819 /* right on the subdivision, so the difference is just the subdivision ticks */
3820 ticks += ticks_one_subdivisions_worth;
3823 /* not on subdivision, compute distance to next subdivision */
3825 ticks += ticks_one_subdivisions_worth - mod;
3828 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3829 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3830 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3831 // But I'm keeping it around, until we determine there are no terrible consequences.
3832 // if (ticks >= BBT_Time::ticks_per_beat) {
3833 // ticks -= BBT_Time::ticks_per_beat;
3836 } else if (dir < 0) {
3838 /* round to previous (or same iff dir == RoundDownMaybe) */
3840 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3842 if (difference == 0 && dir == RoundDownAlways) {
3843 /* right on the subdivision, but force-rounding down,
3844 so the difference is just the subdivision ticks */
3845 difference = ticks_one_subdivisions_worth;
3848 if (ticks < difference) {
3849 ticks = BBT_Time::ticks_per_beat - ticks;
3851 ticks -= difference;
3855 /* round to nearest */
3858 /* compute the distance to the previous and next subdivision */
3860 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3862 /* closer to the next subdivision, so shift forward */
3864 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3866 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3868 if (ticks > BBT_Time::ticks_per_beat) {
3870 ticks -= BBT_Time::ticks_per_beat;
3871 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3874 } else if (rem > 0) {
3876 /* closer to previous subdivision, so shift backward */
3880 /* can't go backwards past zero, so ... */
3883 /* step back to previous beat */
3885 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3886 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3888 ticks = lrint (ticks - rem);
3889 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3892 /* on the subdivision, do nothing */
3896 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3902 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3904 Glib::Threads::RWLock::ReaderLock lm (lock);
3906 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3907 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3912 /* find bar previous to 'frame' */
3917 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3919 } else if (dir > 0) {
3920 /* find bar following 'frame' */
3924 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3926 /* true rounding: find nearest bar */
3927 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3930 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3932 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3934 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3945 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3946 } else if (dir > 0) {
3947 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3949 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3958 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3959 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3961 Glib::Threads::RWLock::ReaderLock lm (lock);
3962 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3964 /* although the map handles negative beats, bbt doesn't. */
3969 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3973 while (pos >= 0 && pos < upper) {
3974 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3975 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3976 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3977 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3979 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
3983 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3988 bbt.bars -= bbt.bars % bar_mod;
3992 while (pos >= 0 && pos < upper) {
3993 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3994 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3995 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3996 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
3997 bbt.bars += bar_mod;
4003 TempoMap::tempo_section_at_frame (framepos_t frame) const
4005 Glib::Threads::RWLock::ReaderLock lm (lock);
4007 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4011 TempoMap::tempo_section_at_frame (framepos_t frame)
4013 Glib::Threads::RWLock::ReaderLock lm (lock);
4015 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4019 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4021 TempoSection* prev = 0;
4025 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4027 if ((*i)->is_tempo()) {
4028 t = static_cast<TempoSection*> (*i);
4032 if (prev && t->minute() > minute) {
4042 abort(); /*NOTREACHED*/
4048 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4050 TempoSection* prev = 0;
4054 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4056 if ((*i)->is_tempo()) {
4057 t = static_cast<TempoSection*> (*i);
4061 if (prev && t->minute() > minute) {
4071 abort(); /*NOTREACHED*/
4077 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4079 TempoSection* prev_t = 0;
4080 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4084 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4085 if ((*i)->is_tempo()) {
4086 t = static_cast<TempoSection*> (*i);
4092 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4102 /* don't use this to calculate length (the tempo is only correct for this frame).
4103 do that stuff based on the beat_at_frame and frame_at_beat api
4106 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4108 Glib::Threads::RWLock::ReaderLock lm (lock);
4110 const TempoSection* ts_at = 0;
4111 const TempoSection* ts_after = 0;
4112 Metrics::const_iterator i;
4115 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4117 if ((*i)->is_tempo()) {
4118 t = static_cast<TempoSection*> (*i);
4122 if (ts_at && (*i)->frame() > frame) {
4132 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4134 /* must be treated as constant tempo */
4135 return ts_at->frames_per_quarter_note (_frame_rate);
4139 TempoMap::meter_section_at_frame (framepos_t frame) const
4141 Glib::Threads::RWLock::ReaderLock lm (lock);
4142 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4146 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4148 Metrics::const_iterator i;
4149 MeterSection* prev = 0;
4153 for (i = metrics.begin(); i != metrics.end(); ++i) {
4155 if (!(*i)->is_tempo()) {
4156 m = static_cast<MeterSection*> (*i);
4158 if (prev && (*i)->minute() > minute) {
4168 abort(); /*NOTREACHED*/
4175 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4177 MeterSection* prev_m = 0;
4179 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4181 if (!(*i)->is_tempo()) {
4182 m = static_cast<MeterSection*> (*i);
4183 if (prev_m && m->beat() > beat) {
4194 TempoMap::meter_section_at_beat (double beat) const
4196 Glib::Threads::RWLock::ReaderLock lm (lock);
4197 return meter_section_at_beat_locked (_metrics, beat);
4201 TempoMap::meter_at_frame (framepos_t frame) const
4203 TempoMetric m (metric_at (frame));
4208 TempoMap::fix_legacy_session ()
4210 MeterSection* prev_m = 0;
4211 TempoSection* prev_t = 0;
4213 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4217 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4219 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4222 m->set_minute (0.0);
4223 m->set_position_lock_style (AudioTime);
4228 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4229 + (m->bbt().beats - 1)
4230 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4232 m->set_beat (start);
4233 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4234 + (m->bbt().beats - 1)
4235 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4236 m->set_pulse (start_beat / prev_m->note_divisor());
4239 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4247 t->set_minute (0.0);
4248 t->set_position_lock_style (AudioTime);
4254 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4255 + (t->legacy_bbt().beats - 1)
4256 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4258 t->set_pulse (beat / prev_m->note_divisor());
4260 /* really shouldn't happen but.. */
4261 t->set_pulse (beat / 4.0);
4270 TempoMap::get_state ()
4272 Metrics::const_iterator i;
4273 XMLNode *root = new XMLNode ("TempoMap");
4276 Glib::Threads::RWLock::ReaderLock lm (lock);
4277 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4278 root->add_child_nocopy ((*i)->get_state());
4286 TempoMap::set_state (const XMLNode& node, int /*version*/)
4289 Glib::Threads::RWLock::WriterLock lm (lock);
4292 XMLNodeConstIterator niter;
4293 Metrics old_metrics (_metrics);
4296 nlist = node.children();
4298 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4299 XMLNode* child = *niter;
4301 if (child->name() == TempoSection::xml_state_node_name) {
4304 TempoSection* ts = new TempoSection (*child, _frame_rate);
4305 _metrics.push_back (ts);
4308 catch (failed_constructor& err){
4309 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4310 _metrics = old_metrics;
4311 old_metrics.clear();
4315 } else if (child->name() == MeterSection::xml_state_node_name) {
4318 MeterSection* ms = new MeterSection (*child, _frame_rate);
4319 _metrics.push_back (ms);
4322 catch (failed_constructor& err) {
4323 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4324 _metrics = old_metrics;
4325 old_metrics.clear();
4331 if (niter == nlist.end()) {
4332 MetricSectionSorter cmp;
4333 _metrics.sort (cmp);
4336 /* check for legacy sessions where bbt was the base musical unit for tempo */
4337 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4339 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4340 if (t->legacy_bbt().bars != 0) {
4341 fix_legacy_session();
4348 /* check for multiple tempo/meters at the same location, which
4349 ardour2 somehow allowed.
4352 Metrics::iterator prev = _metrics.end();
4353 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4354 if (prev != _metrics.end()) {
4356 MeterSection* prev_m;
4358 TempoSection* prev_t;
4359 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4360 if (prev_m->pulse() == ms->pulse()) {
4361 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4362 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4365 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4366 if (prev_t->pulse() == ts->pulse()) {
4367 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4368 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4376 recompute_map (_metrics);
4378 Metrics::const_iterator d = old_metrics.begin();
4379 while (d != old_metrics.end()) {
4383 old_metrics.clear ();
4386 PropertyChanged (PropertyChange ());
4392 TempoMap::dump (std::ostream& o) const
4394 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4395 const MeterSection* m;
4396 const TempoSection* t;
4397 const TempoSection* prev_t = 0;
4399 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4401 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4402 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4403 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4404 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4405 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4407 o << " current : " << t->note_types_per_minute()
4408 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4409 o << " previous : " << prev_t->note_types_per_minute()
4410 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4411 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4412 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4413 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4414 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4417 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4418 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4419 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4420 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4423 o << "------" << std::endl;
4427 TempoMap::n_tempos() const
4429 Glib::Threads::RWLock::ReaderLock lm (lock);
4432 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4433 if ((*i)->is_tempo()) {
4442 TempoMap::n_meters() const
4444 Glib::Threads::RWLock::ReaderLock lm (lock);
4447 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4448 if (!(*i)->is_tempo()) {
4457 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4459 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4460 if ((*i)->frame() >= where && !(*i)->initial ()) {
4464 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4465 gui_set_meter_position (ms, (*i)->frame() + amount);
4468 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4469 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4474 PropertyChanged (PropertyChange ());
4478 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4482 std::list<MetricSection*> metric_kill_list;
4484 TempoSection* last_tempo = NULL;
4485 MeterSection* last_meter = NULL;
4486 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4487 bool meter_after = false; // is there a meter marker likewise?
4489 Glib::Threads::RWLock::WriterLock lm (lock);
4490 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4491 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4492 metric_kill_list.push_back(*i);
4493 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4496 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4500 else if ((*i)->frame() >= where) {
4501 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4502 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4503 if ((*i)->frame() == where) {
4504 // marker was immediately after end of range
4505 tempo_after = dynamic_cast<TempoSection*> (*i);
4506 meter_after = dynamic_cast<MeterSection*> (*i);
4512 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4513 if (last_tempo && !tempo_after) {
4514 metric_kill_list.remove(last_tempo);
4515 last_tempo->set_minute (minute_at_frame (where));
4518 if (last_meter && !meter_after) {
4519 metric_kill_list.remove(last_meter);
4520 last_meter->set_minute (minute_at_frame (where));
4524 //remove all the remaining metrics
4525 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4526 _metrics.remove(*i);
4531 recompute_map (_metrics);
4534 PropertyChanged (PropertyChange ());
4538 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4539 * pos can be -ve, if required.
4542 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4544 Glib::Threads::RWLock::ReaderLock lm (lock);
4545 const double frame_qn = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
4547 return frame_at_minute (minute_at_quarter_note_locked (_metrics, frame_qn + beats.to_double()));
4551 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4553 Glib::Threads::RWLock::ReaderLock lm (lock);
4555 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4556 pos_bbt.ticks += op.ticks;
4557 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4559 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4561 pos_bbt.beats += op.beats;
4562 /* the meter in effect will start on the bar */
4563 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();
4564 while (pos_bbt.beats >= divisions_per_bar + 1) {
4566 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4567 pos_bbt.beats -= divisions_per_bar;
4569 pos_bbt.bars += op.bars;
4571 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4574 /** Count the number of beats that are equivalent to distance when going forward,
4578 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4580 Glib::Threads::RWLock::ReaderLock lm (lock);
4582 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4586 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4592 operator<< (std::ostream& o, const Meter& m) {
4593 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4597 operator<< (std::ostream& o, const Tempo& t) {
4598 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4602 operator<< (std::ostream& o, const MetricSection& section) {
4604 o << "MetricSection @ " << section.frame() << ' ';
4606 const TempoSection* ts;
4607 const MeterSection* ms;
4609 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4610 o << *((const Tempo*) ts);
4611 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4612 o << *((const Meter*) ms);