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_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_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_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_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->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;
2249 const double minute = minute_at_frame (frame);
2251 Glib::Threads::RWLock::ReaderLock lm (lock);
2253 return bbt_at_minute_locked (_metrics, minute);
2257 TempoMap::bbt_at_frame_rt (framepos_t frame)
2259 const double minute = minute_at_frame (frame);
2261 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2264 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2267 return bbt_at_minute_locked (_metrics, minute);
2271 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2281 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2282 MeterSection* prev_m = 0;
2283 MeterSection* next_m = 0;
2287 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2288 if (!(*i)->is_tempo()) {
2289 m = static_cast<MeterSection*> (*i);
2290 if (prev_m && m->minute() > minute) {
2298 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2300 /* handle frame before first meter */
2301 if (minute < prev_m->minute()) {
2304 /* audio locked meters fake their beat */
2305 if (next_m && next_m->beat() < beat) {
2306 beat = next_m->beat();
2309 beat = max (0.0, beat);
2311 const double beats_in_ms = beat - prev_m->beat();
2312 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2313 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2314 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2315 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2319 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2320 ret.beats = (uint32_t) floor (remaining_beats);
2321 ret.bars = total_bars;
2323 /* 0 0 0 to 1 1 0 - based mapping*/
2327 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2329 ret.ticks -= BBT_Time::ticks_per_beat;
2332 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2340 /** Returns the frame position corresponding to the supplied BBT time.
2341 * @param bbt the position in BBT time.
2342 * @return the frame position at bbt.
2346 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2349 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2353 if (bbt.beats < 1) {
2354 throw std::logic_error ("beats are counted from one");
2359 Glib::Threads::RWLock::ReaderLock lm (lock);
2360 minute = minute_at_bbt_locked (_metrics, bbt);
2363 return frame_at_minute (minute);
2366 /* meter & tempo section based */
2368 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2370 /* HOLD THE READER LOCK */
2372 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2377 * Returns the quarter-note beat position corresponding to the supplied frame.
2379 * @param frame the position in frames.
2380 * @return The quarter-note position of the supplied frame. Ignores meter.
2384 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2386 const double minute = minute_at_frame (frame);
2388 Glib::Threads::RWLock::ReaderLock lm (lock);
2390 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2394 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2396 const double minute = minute_at_frame (frame);
2398 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2401 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2404 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2408 * Returns the frame position corresponding to the supplied quarter-note beat.
2410 * @param quarter_note the quarter-note position.
2411 * @return the frame position of the supplied quarter-note. Ignores meter.
2416 TempoMap::frame_at_quarter_note (const double quarter_note) const
2420 Glib::Threads::RWLock::ReaderLock lm (lock);
2422 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2425 return frame_at_minute (minute);
2428 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2429 * @param beat The BBT (meter-based) beat.
2430 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2432 * a quarter-note may be compared with and assigned to Evoral::Beats.
2436 TempoMap::quarter_note_at_beat (const double beat) const
2438 Glib::Threads::RWLock::ReaderLock lm (lock);
2440 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2443 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2444 * @param quarter_note The position in quarter-note beats.
2445 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2447 * a quarter-note is the musical unit of Evoral::Beats.
2451 TempoMap::beat_at_quarter_note (const double quarter_note) const
2453 Glib::Threads::RWLock::ReaderLock lm (lock);
2455 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2458 /** Returns the duration in frames between two supplied quarter-note beat positions.
2459 * @param start the first position in quarter-note beats.
2460 * @param end the end position in quarter-note beats.
2461 * @return the frame distance ober the quarter-note beats duration.
2463 * use this rather than e.g.
2464 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2465 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2469 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2474 Glib::Threads::RWLock::ReaderLock lm (lock);
2475 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2478 return frame_at_minute (minutes);
2482 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2485 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2489 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2491 Glib::Threads::RWLock::ReaderLock lm (lock);
2493 return quarter_notes_between_frames_locked (_metrics, start, end);
2497 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2499 const TempoSection* prev_t = 0;
2501 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2504 if ((*i)->is_tempo()) {
2505 t = static_cast<TempoSection*> (*i);
2509 if (prev_t && t->frame() > start) {
2516 const double start_qn = prev_t->pulse_at_frame (start);
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() > end) {
2532 const double end_qn = prev_t->pulse_at_frame (end);
2534 return (end_qn - start_qn) * 4.0;
2538 TempoMap::check_solved (const Metrics& metrics) const
2540 TempoSection* prev_t = 0;
2541 MeterSection* prev_m = 0;
2543 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2546 if ((*i)->is_tempo()) {
2547 t = static_cast<TempoSection*> (*i);
2552 /* check ordering */
2553 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2557 /* precision check ensures tempo and frames align.*/
2558 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2559 if (!t->locked_to_meter()) {
2564 /* gradient limit - who knows what it should be?
2565 things are also ok (if a little chaotic) without this
2567 if (fabs (prev_t->c()) > 1000.0) {
2568 //std::cout << "c : " << prev_t->c() << std::endl;
2575 if (!(*i)->is_tempo()) {
2576 m = static_cast<MeterSection*> (*i);
2577 if (prev_m && m->position_lock_style() == AudioTime) {
2578 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2579 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2580 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2582 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2596 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2598 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2600 if ((*i)->is_tempo()) {
2601 t = static_cast<TempoSection*> (*i);
2602 if (t->locked_to_meter()) {
2603 t->set_active (true);
2604 } else if (t->position_lock_style() == AudioTime) {
2605 if (t->frame() < frame) {
2606 t->set_active (false);
2607 t->set_pulse (-1.0);
2608 } else if (t->frame() > frame) {
2609 t->set_active (true);
2610 } else if (t->frame() == frame) {
2620 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2622 TempoSection* prev_t = 0;
2623 TempoSection* section_prev = 0;
2624 double first_m_minute = 0.0;
2625 const bool sml = section->locked_to_meter();
2627 /* can't move a tempo before the first meter */
2628 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2630 if (!(*i)->is_tempo()) {
2631 m = static_cast<MeterSection*> (*i);
2633 first_m_minute = m->minute();
2638 if (!section->initial() && minute <= first_m_minute) {
2642 section->set_active (true);
2643 section->set_minute (minute);
2645 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2647 if ((*i)->is_tempo()) {
2648 t = static_cast<TempoSection*> (*i);
2660 if (t->frame() == frame_at_minute (minute)) {
2664 const bool tlm = t->position_lock_style() == MusicTime;
2666 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2667 section_prev = prev_t;
2669 section_prev->set_c (section_prev->compute_c_minute (section->note_types_per_minute(), minute));
2670 if (!section->locked_to_meter()) {
2671 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2676 if (t->position_lock_style() == MusicTime) {
2677 prev_t->set_c (prev_t->compute_c_pulse (t->note_types_per_minute(), t->pulse()));
2678 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2680 prev_t->set_c (prev_t->compute_c_minute (t->note_types_per_minute(), t->minute()));
2681 if (!t->locked_to_meter()) {
2682 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2691 recompute_tempi (imaginary);
2693 if (check_solved (imaginary)) {
2696 dunp (imaginary, std::cout);
2700 MetricSectionFrameSorter fcmp;
2701 imaginary.sort (fcmp);
2703 recompute_tempi (imaginary);
2705 if (check_solved (imaginary)) {
2713 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2715 TempoSection* prev_t = 0;
2716 TempoSection* section_prev = 0;
2718 section->set_pulse (pulse);
2720 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2722 if ((*i)->is_tempo()) {
2723 t = static_cast<TempoSection*> (*i);
2734 section_prev = prev_t;
2738 if (t->position_lock_style() == MusicTime) {
2739 prev_t->set_c (prev_t->compute_c_pulse (t->note_types_per_minute(), t->pulse()));
2740 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2742 prev_t->set_c (prev_t->compute_c_minute (t->note_types_per_minute(), t->minute()));
2743 if (!t->locked_to_meter()) {
2744 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2753 section_prev->set_c (section_prev->compute_c_pulse (section->note_types_per_minute(), pulse));
2754 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2758 recompute_tempi (imaginary);
2760 if (check_solved (imaginary)) {
2763 dunp (imaginary, std::cout);
2767 MetricSectionSorter cmp;
2768 imaginary.sort (cmp);
2770 recompute_tempi (imaginary);
2772 * XX need a restriction here, but only for this case,
2773 * as audio locked tempos don't interact in the same way.
2775 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2777 * |50 bpm |250 bpm |60 bpm
2778 * drag 250 to the pulse after 60->
2779 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2781 if (check_solved (imaginary)) {
2789 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2791 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2792 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2793 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2797 if (section->initial()) {
2798 /* lock the first tempo to our first meter */
2799 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2804 TempoSection* meter_locked_tempo = 0;
2806 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2808 if ((*ii)->is_tempo()) {
2809 t = static_cast<TempoSection*> (*ii);
2810 if (t->locked_to_meter() && t->frame() == section->frame()) {
2811 meter_locked_tempo = t;
2817 if (!meter_locked_tempo) {
2821 MeterSection* prev_m = 0;
2823 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2824 bool solved = false;
2826 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2828 if (!(*i)->is_tempo()) {
2829 m = static_cast<MeterSection*> (*i);
2831 if (prev_m && !section->initial()) {
2832 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2833 if (beats + prev_m->beat() < section->beat()) {
2834 /* set the section pulse according to its musical position,
2835 * as an earlier time than this has been requested.
2837 const double new_pulse = ((section->beat() - prev_m->beat())
2838 / prev_m->note_divisor()) + prev_m->pulse();
2840 tempo_copy->set_position_lock_style (MusicTime);
2841 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2842 meter_locked_tempo->set_position_lock_style (MusicTime);
2843 section->set_position_lock_style (MusicTime);
2844 section->set_pulse (new_pulse);
2845 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2846 meter_locked_tempo->set_position_lock_style (AudioTime);
2847 section->set_position_lock_style (AudioTime);
2848 section->set_minute (meter_locked_tempo->minute());
2854 Metrics::const_iterator d = future_map.begin();
2855 while (d != future_map.end()) {
2864 /* all is ok. set section's locked tempo if allowed.
2865 possibly disallow if there is an adjacent audio-locked tempo.
2866 XX this check could possibly go. its never actually happened here.
2868 MeterSection* meter_copy = const_cast<MeterSection*>
2869 (&meter_section_at_minute_locked (future_map, section->minute()));
2871 meter_copy->set_minute (minute);
2873 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2874 section->set_minute (minute);
2875 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2876 / prev_m->note_divisor()) + prev_m->pulse());
2877 solve_map_minute (imaginary, meter_locked_tempo, minute);
2882 Metrics::const_iterator d = future_map.begin();
2883 while (d != future_map.end()) {
2893 /* initial (first meter atm) */
2895 tempo_copy->set_minute (minute);
2896 tempo_copy->set_pulse (0.0);
2898 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2899 section->set_minute (minute);
2900 meter_locked_tempo->set_minute (minute);
2901 meter_locked_tempo->set_pulse (0.0);
2902 solve_map_minute (imaginary, meter_locked_tempo, minute);
2907 Metrics::const_iterator d = future_map.begin();
2908 while (d != future_map.end()) {
2917 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2918 section->set_beat (b_bbt);
2919 section->set_pulse (0.0);
2929 MetricSectionFrameSorter fcmp;
2930 imaginary.sort (fcmp);
2932 recompute_meters (imaginary);
2938 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2940 /* disallow setting section to an existing meter's bbt */
2941 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2943 if (!(*i)->is_tempo()) {
2944 m = static_cast<MeterSection*> (*i);
2945 if (m != section && m->bbt().bars == when.bars) {
2951 MeterSection* prev_m = 0;
2952 MeterSection* section_prev = 0;
2954 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2956 if (!(*i)->is_tempo()) {
2957 m = static_cast<MeterSection*> (*i);
2963 pair<double, BBT_Time> b_bbt;
2964 double new_pulse = 0.0;
2966 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2967 section_prev = prev_m;
2969 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2970 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2971 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2973 section->set_beat (b_bbt);
2974 section->set_pulse (pulse);
2975 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2979 if (m->position_lock_style() == AudioTime) {
2980 TempoSection* meter_locked_tempo = 0;
2982 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2984 if ((*ii)->is_tempo()) {
2985 t = static_cast<TempoSection*> (*ii);
2986 if (t->locked_to_meter() && t->frame() == m->frame()) {
2987 meter_locked_tempo = t;
2993 if (!meter_locked_tempo) {
2998 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3000 if (beats + prev_m->beat() != m->beat()) {
3001 /* tempo/ meter change caused a change in beat (bar). */
3003 /* the user has requested that the previous section of music overlaps this one.
3004 we have no choice but to change the bar number here, as being locked to audio means
3005 we must stay where we are on the timeline.
3007 beats = m->beat() - prev_m->beat();
3008 b_bbt = make_pair (beats + prev_m->beat()
3009 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3010 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3012 } else if (!m->initial()) {
3013 b_bbt = make_pair (m->beat(), m->bbt());
3014 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3017 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3020 meter_locked_tempo->set_pulse (new_pulse);
3021 m->set_beat (b_bbt);
3022 m->set_pulse (new_pulse);
3026 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3027 if (beats + prev_m->beat() != m->beat()) {
3028 /* tempo/ meter change caused a change in beat (bar). */
3029 b_bbt = make_pair (beats + prev_m->beat()
3030 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3032 b_bbt = make_pair (beats + prev_m->beat()
3035 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3036 m->set_beat (b_bbt);
3037 m->set_pulse (new_pulse);
3038 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3045 if (!section_prev) {
3047 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3048 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3049 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3051 section->set_beat (b_bbt);
3052 section->set_pulse (pulse);
3053 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3056 MetricSectionSorter cmp;
3057 imaginary.sort (cmp);
3059 recompute_meters (imaginary);
3064 /** places a copy of _metrics into copy and returns a pointer
3065 * to section's equivalent in copy.
3068 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3070 TempoSection* ret = 0;
3072 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3075 if ((*i)->is_tempo()) {
3076 t = static_cast<TempoSection*> (*i);
3078 ret = new TempoSection (*t);
3079 copy.push_back (ret);
3083 TempoSection* cp = new TempoSection (*t);
3084 copy.push_back (cp);
3086 if (!(*i)->is_tempo()) {
3087 m = static_cast<MeterSection *> (*i);
3088 MeterSection* cp = new MeterSection (*m);
3089 copy.push_back (cp);
3097 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3099 MeterSection* ret = 0;
3101 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3104 if ((*i)->is_tempo()) {
3105 t = static_cast<TempoSection*> (*i);
3106 TempoSection* cp = new TempoSection (*t);
3107 copy.push_back (cp);
3110 if (!(*i)->is_tempo()) {
3111 m = static_cast<MeterSection *> (*i);
3113 ret = new MeterSection (*m);
3114 copy.push_back (ret);
3117 MeterSection* cp = new MeterSection (*m);
3118 copy.push_back (cp);
3125 /** answers the question "is this a valid beat position for this tempo section?".
3126 * it returns true if the tempo section can be moved to the requested bbt position,
3127 * leaving the tempo map in a solved state.
3128 * @param ts the tempo section to be moved
3129 * @param bbt the requested new position for the tempo section
3130 * @return true if the tempo section can be moved to the position, otherwise false.
3133 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3136 TempoSection* tempo_copy = 0;
3139 Glib::Threads::RWLock::ReaderLock lm (lock);
3140 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3146 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3148 Metrics::const_iterator d = copy.begin();
3149 while (d != copy.end()) {
3158 * 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,
3159 * taking any possible reordering as a consequence of this into account.
3160 * @param section - the section to be altered
3161 * @param bbt - the BBT time where the altered tempo will fall
3162 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3164 pair<double, framepos_t>
3165 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3168 pair<double, framepos_t> ret = make_pair (0.0, 0);
3170 Glib::Threads::RWLock::ReaderLock lm (lock);
3172 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3174 const double beat = beat_at_bbt_locked (future_map, bbt);
3176 if (section->position_lock_style() == AudioTime) {
3177 tempo_copy->set_position_lock_style (MusicTime);
3180 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3181 ret.first = tempo_copy->pulse();
3182 ret.second = tempo_copy->frame();
3184 ret.first = section->pulse();
3185 ret.second = section->frame();
3188 Metrics::const_iterator d = future_map.begin();
3189 while (d != future_map.end()) {
3196 /** moves a TempoSection to a specified position.
3197 * @param ts - the section to be moved
3198 * @param frame - the new position in frames for the tempo
3199 * @param sub_num - the snap division to use if using musical time.
3201 * if sub_num is non-zero, the frame position is used to calculate an exact
3204 * -1 | snap to bars (meter-based)
3205 * 0 | no snap - use audio frame for musical position
3206 * 1 | snap to meter-based (BBT) beat
3207 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3209 * this follows the snap convention in the gui.
3210 * if sub_num is zero, the musical position will be taken from the supplied frame.
3213 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3217 if (ts->position_lock_style() == MusicTime) {
3219 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3220 Glib::Threads::RWLock::WriterLock lm (lock);
3221 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3223 tempo_copy->set_position_lock_style (AudioTime);
3225 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3226 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3227 const double pulse = pulse_at_beat_locked (future_map, beat);
3229 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3230 solve_map_pulse (_metrics, ts, pulse);
3231 recompute_meters (_metrics);
3239 Glib::Threads::RWLock::WriterLock lm (lock);
3240 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3242 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3244 /* We're moving the object that defines the grid while snapping to it...
3245 * Placing the ts at the beat corresponding to the requested frame may shift the
3246 * grid in such a way that the mouse is left hovering over a completerly different division,
3247 * causing jittering when the mouse next moves (esp. large tempo deltas).
3249 * This alters the snap behaviour slightly in that we snap to beat divisions
3250 * in the future map rather than the existing one.
3252 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3253 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3255 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3256 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3257 ts->set_pulse (qn / 4.0);
3258 recompute_meters (_metrics);
3261 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3262 recompute_meters (_metrics);
3268 Metrics::const_iterator d = future_map.begin();
3269 while (d != future_map.end()) {
3274 MetricPositionChanged (PropertyChange ()); // Emit Signal
3277 /** moves a MeterSection to a specified position.
3278 * @param ms - the section to be moved
3279 * @param frame - the new position in frames for the meter
3281 * as a meter cannot snap to anything but bars,
3282 * the supplied frame is rounded to the nearest bar, possibly
3283 * leaving the meter position unchanged.
3286 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3290 if (ms->position_lock_style() == AudioTime) {
3293 Glib::Threads::RWLock::WriterLock lm (lock);
3294 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3296 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3297 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3298 recompute_tempi (_metrics);
3303 Glib::Threads::RWLock::WriterLock lm (lock);
3304 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3306 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3307 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3309 if (solve_map_bbt (future_map, copy, bbt)) {
3310 solve_map_bbt (_metrics, ms, bbt);
3311 recompute_tempi (_metrics);
3316 Metrics::const_iterator d = future_map.begin();
3317 while (d != future_map.end()) {
3322 MetricPositionChanged (PropertyChange ()); // Emit Signal
3326 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3329 bool can_solve = false;
3331 Glib::Threads::RWLock::WriterLock lm (lock);
3332 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3333 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3334 recompute_tempi (future_map);
3336 if (check_solved (future_map)) {
3337 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3338 recompute_map (_metrics);
3343 Metrics::const_iterator d = future_map.begin();
3344 while (d != future_map.end()) {
3349 MetricPositionChanged (PropertyChange ()); // Emit Signal
3355 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3358 Ts (future prev_t) Tnext
3361 |----------|----------
3368 Glib::Threads::RWLock::WriterLock lm (lock);
3374 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3375 TempoSection* prev_to_prev_t = 0;
3376 const frameoffset_t fr_off = end_frame - frame;
3380 if (prev_t->pulse() > 0.0) {
3381 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3384 TempoSection* next_t = 0;
3385 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3386 TempoSection* t = 0;
3387 if ((*i)->is_tempo()) {
3388 t = static_cast<TempoSection*> (*i);
3389 if (t->frame() > ts->frame()) {
3395 /* minimum allowed measurement distance in frames */
3396 const framepos_t min_dframe = 2;
3398 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3399 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3401 double contribution = 0.0;
3403 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3404 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3407 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3409 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3410 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3414 if (prev_t->type() == TempoSection::Constant || prev_t->c() == 0.0) {
3416 if (prev_t->position_lock_style() == MusicTime) {
3417 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3418 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3420 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3421 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3423 new_bpm = prev_t->note_types_per_minute();
3426 /* prev to prev is irrelevant */
3428 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3429 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3431 new_bpm = prev_t->note_types_per_minute();
3436 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3437 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3439 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3440 / (double) ((end_frame) - prev_to_prev_t->frame()));
3442 new_bpm = prev_t->note_types_per_minute();
3445 /* prev_to_prev_t is irrelevant */
3447 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3448 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3450 new_bpm = prev_t->note_types_per_minute();
3456 double frame_ratio = 1.0;
3457 double pulse_ratio = 1.0;
3458 const double pulse_pos = frame;
3460 if (prev_to_prev_t) {
3461 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3462 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3464 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3465 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3468 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3469 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3471 pulse_ratio = (start_pulse / end_pulse);
3473 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3476 /* don't clamp and proceed here.
3477 testing has revealed that this can go negative,
3478 which is an entirely different thing to just being too low.
3480 if (new_bpm < 0.5) {
3483 new_bpm = min (new_bpm, (double) 1000.0);
3484 prev_t->set_note_types_per_minute (new_bpm);
3485 recompute_tempi (future_map);
3486 recompute_meters (future_map);
3488 if (check_solved (future_map)) {
3489 ts->set_note_types_per_minute (new_bpm);
3490 recompute_tempi (_metrics);
3491 recompute_meters (_metrics);
3495 Metrics::const_iterator d = future_map.begin();
3496 while (d != future_map.end()) {
3501 MetricPositionChanged (PropertyChange ()); // Emit Signal
3504 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3505 * the supplied frame, possibly returning a negative value.
3507 * @param frame The session frame position.
3508 * @param sub_num The subdivision to use when rounding the beat.
3509 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3510 * Positive integers indicate quarter note (non BBT) divisions.
3511 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3512 * @return The beat position of the supplied frame.
3514 * when working to a musical grid, the use of sub_nom indicates that
3515 * the position should be interpreted musically.
3517 * it effectively snaps to meter bars, meter beats or quarter note divisions
3518 * (as per current gui convention) and returns a musical position independent of frame rate.
3520 * If the supplied frame lies before the first meter, the return will be negative,
3521 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3522 * the continuation of the tempo curve (backwards).
3524 * This function is sensitive to tempo and meter.
3527 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3529 Glib::Threads::RWLock::ReaderLock lm (lock);
3531 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3535 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3537 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3540 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3541 * the supplied frame, possibly returning a negative value.
3543 * @param frame The session frame position.
3544 * @param sub_num The subdivision to use when rounding the quarter note.
3545 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3546 * Positive integers indicate quarter note (non BBT) divisions.
3547 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3548 * @return The quarter note position of the supplied frame.
3550 * When working to a musical grid, the use of sub_nom indicates that
3551 * the frame position should be interpreted musically.
3553 * it effectively snaps to meter bars, meter beats or quarter note divisions
3554 * (as per current gui convention) and returns a musical position independent of frame rate.
3556 * If the supplied frame lies before the first meter, the return will be negative,
3557 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3558 * the continuation of the tempo curve (backwards).
3560 * This function is tempo-sensitive.
3563 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3565 Glib::Threads::RWLock::ReaderLock lm (lock);
3567 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3571 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3573 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3576 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3577 } else if (sub_num == 1) {
3578 /* the gui requested exact musical (BBT) beat */
3579 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5)) * 4.0);
3580 } else if (sub_num == -1) {
3582 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3586 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3588 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3590 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3600 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3601 * @param pos the frame position in the tempo map.
3602 * @param bbt the distance in BBT time from pos to calculate.
3603 * @param dir the rounding direction..
3604 * @return the duration in frames between pos and bbt
3607 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3609 Glib::Threads::RWLock::ReaderLock lm (lock);
3611 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3613 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3616 pos_bbt.bars += bbt.bars;
3618 pos_bbt.ticks += bbt.ticks;
3619 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3621 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3624 pos_bbt.beats += bbt.beats;
3625 if ((double) pos_bbt.beats > divisions) {
3627 pos_bbt.beats -= divisions;
3629 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3631 return pos_bbt_frame - pos;
3635 if (pos_bbt.bars <= bbt.bars) {
3638 pos_bbt.bars -= bbt.bars;
3641 if (pos_bbt.ticks < bbt.ticks) {
3642 if (pos_bbt.bars > 1) {
3643 if (pos_bbt.beats == 1) {
3645 pos_bbt.beats = divisions;
3649 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3655 pos_bbt.ticks -= bbt.ticks;
3658 if (pos_bbt.beats <= bbt.beats) {
3659 if (pos_bbt.bars > 1) {
3661 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3666 pos_bbt.beats -= bbt.beats;
3669 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3676 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3678 return round_to_type (fr, dir, Bar);
3682 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3684 return round_to_type (fr, dir, Beat);
3688 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3690 Glib::Threads::RWLock::ReaderLock lm (lock);
3691 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3692 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3693 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3695 ticks -= beats * BBT_Time::ticks_per_beat;
3698 /* round to next (or same iff dir == RoundUpMaybe) */
3700 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3702 if (mod == 0 && dir == RoundUpMaybe) {
3703 /* right on the subdivision, which is fine, so do nothing */
3705 } else if (mod == 0) {
3706 /* right on the subdivision, so the difference is just the subdivision ticks */
3707 ticks += ticks_one_subdivisions_worth;
3710 /* not on subdivision, compute distance to next subdivision */
3712 ticks += ticks_one_subdivisions_worth - mod;
3715 if (ticks >= BBT_Time::ticks_per_beat) {
3716 ticks -= BBT_Time::ticks_per_beat;
3718 } else if (dir < 0) {
3720 /* round to previous (or same iff dir == RoundDownMaybe) */
3722 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3724 if (difference == 0 && dir == RoundDownAlways) {
3725 /* right on the subdivision, but force-rounding down,
3726 so the difference is just the subdivision ticks */
3727 difference = ticks_one_subdivisions_worth;
3730 if (ticks < difference) {
3731 ticks = BBT_Time::ticks_per_beat - ticks;
3733 ticks -= difference;
3737 /* round to nearest */
3740 /* compute the distance to the previous and next subdivision */
3742 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3744 /* closer to the next subdivision, so shift forward */
3746 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3748 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3750 if (ticks > BBT_Time::ticks_per_beat) {
3752 ticks -= BBT_Time::ticks_per_beat;
3753 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3756 } else if (rem > 0) {
3758 /* closer to previous subdivision, so shift backward */
3762 /* can't go backwards past zero, so ... */
3765 /* step back to previous beat */
3767 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3768 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3770 ticks = lrint (ticks - rem);
3771 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3774 /* on the subdivision, do nothing */
3778 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3784 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3786 Glib::Threads::RWLock::ReaderLock lm (lock);
3787 uint32_t ticks = (uint32_t) floor (max (0.0, pulse_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat * 4.0);
3788 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3789 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3791 ticks -= beats * BBT_Time::ticks_per_beat;
3794 /* round to next (or same iff dir == RoundUpMaybe) */
3796 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3798 if (mod == 0 && dir == RoundUpMaybe) {
3799 /* right on the subdivision, which is fine, so do nothing */
3801 } else if (mod == 0) {
3802 /* right on the subdivision, so the difference is just the subdivision ticks */
3803 ticks += ticks_one_subdivisions_worth;
3806 /* not on subdivision, compute distance to next subdivision */
3808 ticks += ticks_one_subdivisions_worth - mod;
3811 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3812 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3813 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3814 // But I'm keeping it around, until we determine there are no terrible consequences.
3815 // if (ticks >= BBT_Time::ticks_per_beat) {
3816 // ticks -= BBT_Time::ticks_per_beat;
3819 } else if (dir < 0) {
3821 /* round to previous (or same iff dir == RoundDownMaybe) */
3823 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3825 if (difference == 0 && dir == RoundDownAlways) {
3826 /* right on the subdivision, but force-rounding down,
3827 so the difference is just the subdivision ticks */
3828 difference = ticks_one_subdivisions_worth;
3831 if (ticks < difference) {
3832 ticks = BBT_Time::ticks_per_beat - ticks;
3834 ticks -= difference;
3838 /* round to nearest */
3841 /* compute the distance to the previous and next subdivision */
3843 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3845 /* closer to the next subdivision, so shift forward */
3847 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3849 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3851 if (ticks > BBT_Time::ticks_per_beat) {
3853 ticks -= BBT_Time::ticks_per_beat;
3854 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3857 } else if (rem > 0) {
3859 /* closer to previous subdivision, so shift backward */
3863 /* can't go backwards past zero, so ... */
3866 /* step back to previous beat */
3868 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3869 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3871 ticks = lrint (ticks - rem);
3872 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3875 /* on the subdivision, do nothing */
3879 const framepos_t ret_frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
3885 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3887 Glib::Threads::RWLock::ReaderLock lm (lock);
3889 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3890 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3895 /* find bar previous to 'frame' */
3900 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3902 } else if (dir > 0) {
3903 /* find bar following 'frame' */
3907 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3909 /* true rounding: find nearest bar */
3910 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3913 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3915 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3917 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3928 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3929 } else if (dir > 0) {
3930 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3932 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3941 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3942 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3944 Glib::Threads::RWLock::ReaderLock lm (lock);
3945 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3947 /* although the map handles negative beats, bbt doesn't. */
3952 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3956 while (pos >= 0 && pos < upper) {
3957 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3958 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3959 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3960 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3962 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
3966 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3971 bbt.bars -= bbt.bars % bar_mod;
3975 while (pos >= 0 && pos < upper) {
3976 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3977 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3978 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3979 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
3980 bbt.bars += bar_mod;
3986 TempoMap::tempo_section_at_frame (framepos_t frame) const
3988 Glib::Threads::RWLock::ReaderLock lm (lock);
3990 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3994 TempoMap::tempo_section_at_frame (framepos_t frame)
3996 Glib::Threads::RWLock::ReaderLock lm (lock);
3998 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4002 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4004 TempoSection* prev = 0;
4008 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4010 if ((*i)->is_tempo()) {
4011 t = static_cast<TempoSection*> (*i);
4015 if (prev && t->minute() > minute) {
4025 abort(); /*NOTREACHED*/
4031 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4033 TempoSection* prev = 0;
4037 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4039 if ((*i)->is_tempo()) {
4040 t = static_cast<TempoSection*> (*i);
4044 if (prev && t->minute() > minute) {
4054 abort(); /*NOTREACHED*/
4060 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4062 TempoSection* prev_t = 0;
4063 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4067 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4068 if ((*i)->is_tempo()) {
4069 t = static_cast<TempoSection*> (*i);
4075 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4085 /* don't use this to calculate length (the tempo is only correct for this frame).
4086 do that stuff based on the beat_at_frame and frame_at_beat api
4089 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4091 Glib::Threads::RWLock::ReaderLock lm (lock);
4093 const TempoSection* ts_at = 0;
4094 const TempoSection* ts_after = 0;
4095 Metrics::const_iterator i;
4098 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4100 if ((*i)->is_tempo()) {
4101 t = static_cast<TempoSection*> (*i);
4105 if (ts_at && (*i)->frame() > frame) {
4115 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4117 /* must be treated as constant tempo */
4118 return ts_at->frames_per_quarter_note (_frame_rate);
4122 TempoMap::meter_section_at_frame (framepos_t frame) const
4124 Glib::Threads::RWLock::ReaderLock lm (lock);
4125 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4129 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4131 Metrics::const_iterator i;
4132 MeterSection* prev = 0;
4136 for (i = metrics.begin(); i != metrics.end(); ++i) {
4138 if (!(*i)->is_tempo()) {
4139 m = static_cast<MeterSection*> (*i);
4141 if (prev && (*i)->minute() > minute) {
4151 abort(); /*NOTREACHED*/
4158 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4160 MeterSection* prev_m = 0;
4162 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4164 if (!(*i)->is_tempo()) {
4165 m = static_cast<MeterSection*> (*i);
4166 if (prev_m && m->beat() > beat) {
4177 TempoMap::meter_section_at_beat (double beat) const
4179 Glib::Threads::RWLock::ReaderLock lm (lock);
4180 return meter_section_at_beat_locked (_metrics, beat);
4184 TempoMap::meter_at_frame (framepos_t frame) const
4186 TempoMetric m (metric_at (frame));
4191 TempoMap::fix_legacy_session ()
4193 MeterSection* prev_m = 0;
4194 TempoSection* prev_t = 0;
4196 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4200 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4202 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4205 m->set_minute (0.0);
4206 m->set_position_lock_style (AudioTime);
4211 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4212 + (m->bbt().beats - 1)
4213 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4215 m->set_beat (start);
4216 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4217 + (m->bbt().beats - 1)
4218 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4219 m->set_pulse (start_beat / prev_m->note_divisor());
4222 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4230 t->set_minute (0.0);
4231 t->set_position_lock_style (AudioTime);
4237 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4238 + (t->legacy_bbt().beats - 1)
4239 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4241 t->set_pulse (beat / prev_m->note_divisor());
4243 /* really shouldn't happen but.. */
4244 t->set_pulse (beat / 4.0);
4253 TempoMap::get_state ()
4255 Metrics::const_iterator i;
4256 XMLNode *root = new XMLNode ("TempoMap");
4259 Glib::Threads::RWLock::ReaderLock lm (lock);
4260 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4261 root->add_child_nocopy ((*i)->get_state());
4269 TempoMap::set_state (const XMLNode& node, int /*version*/)
4272 Glib::Threads::RWLock::WriterLock lm (lock);
4275 XMLNodeConstIterator niter;
4276 Metrics old_metrics (_metrics);
4279 nlist = node.children();
4281 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4282 XMLNode* child = *niter;
4284 if (child->name() == TempoSection::xml_state_node_name) {
4287 TempoSection* ts = new TempoSection (*child, _frame_rate);
4288 _metrics.push_back (ts);
4291 catch (failed_constructor& err){
4292 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4293 _metrics = old_metrics;
4294 old_metrics.clear();
4298 } else if (child->name() == MeterSection::xml_state_node_name) {
4301 MeterSection* ms = new MeterSection (*child, _frame_rate);
4302 _metrics.push_back (ms);
4305 catch (failed_constructor& err) {
4306 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4307 _metrics = old_metrics;
4308 old_metrics.clear();
4314 if (niter == nlist.end()) {
4315 MetricSectionSorter cmp;
4316 _metrics.sort (cmp);
4319 /* check for legacy sessions where bbt was the base musical unit for tempo */
4320 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4322 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4323 if (t->legacy_bbt().bars != 0) {
4324 fix_legacy_session();
4331 /* check for multiple tempo/meters at the same location, which
4332 ardour2 somehow allowed.
4335 Metrics::iterator prev = _metrics.end();
4336 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4337 if (prev != _metrics.end()) {
4339 MeterSection* prev_m;
4341 TempoSection* prev_t;
4342 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4343 if (prev_m->pulse() == ms->pulse()) {
4344 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4345 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4348 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4349 if (prev_t->pulse() == ts->pulse()) {
4350 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4351 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4359 recompute_map (_metrics);
4361 Metrics::const_iterator d = old_metrics.begin();
4362 while (d != old_metrics.end()) {
4366 old_metrics.clear ();
4369 PropertyChanged (PropertyChange ());
4375 TempoMap::dump (std::ostream& o) const
4377 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4378 const MeterSection* m;
4379 const TempoSection* t;
4380 const TempoSection* prev_t = 0;
4382 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4384 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4385 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4386 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4387 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4388 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4390 o << " current : " << t->note_types_per_minute()
4391 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4392 o << " previous : " << prev_t->note_types_per_minute()
4393 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4394 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4395 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4396 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4397 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4400 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4401 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4402 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4403 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4406 o << "------" << std::endl;
4410 TempoMap::n_tempos() const
4412 Glib::Threads::RWLock::ReaderLock lm (lock);
4415 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4416 if ((*i)->is_tempo()) {
4425 TempoMap::n_meters() const
4427 Glib::Threads::RWLock::ReaderLock lm (lock);
4430 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4431 if (!(*i)->is_tempo()) {
4440 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4442 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4443 if ((*i)->frame() >= where && !(*i)->initial ()) {
4447 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4448 gui_set_meter_position (ms, (*i)->frame() + amount);
4451 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4452 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4457 PropertyChanged (PropertyChange ());
4461 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4465 std::list<MetricSection*> metric_kill_list;
4467 TempoSection* last_tempo = NULL;
4468 MeterSection* last_meter = NULL;
4469 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4470 bool meter_after = false; // is there a meter marker likewise?
4472 Glib::Threads::RWLock::WriterLock lm (lock);
4473 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4474 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4475 metric_kill_list.push_back(*i);
4476 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4479 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4483 else if ((*i)->frame() >= where) {
4484 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4485 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4486 if ((*i)->frame() == where) {
4487 // marker was immediately after end of range
4488 tempo_after = dynamic_cast<TempoSection*> (*i);
4489 meter_after = dynamic_cast<MeterSection*> (*i);
4495 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4496 if (last_tempo && !tempo_after) {
4497 metric_kill_list.remove(last_tempo);
4498 last_tempo->set_minute (minute_at_frame (where));
4501 if (last_meter && !meter_after) {
4502 metric_kill_list.remove(last_meter);
4503 last_meter->set_minute (minute_at_frame (where));
4507 //remove all the remaining metrics
4508 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4509 _metrics.remove(*i);
4514 recompute_map (_metrics);
4517 PropertyChanged (PropertyChange ());
4521 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4522 * pos can be -ve, if required.
4525 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4527 Glib::Threads::RWLock::ReaderLock lm (lock);
4528 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4530 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4534 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4536 Glib::Threads::RWLock::ReaderLock lm (lock);
4538 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4539 pos_bbt.ticks += op.ticks;
4540 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4542 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4544 pos_bbt.beats += op.beats;
4545 /* the meter in effect will start on the bar */
4546 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();
4547 while (pos_bbt.beats >= divisions_per_bar + 1) {
4549 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4550 pos_bbt.beats -= divisions_per_bar;
4552 pos_bbt.bars += op.bars;
4554 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4557 /** Count the number of beats that are equivalent to distance when going forward,
4561 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4563 Glib::Threads::RWLock::ReaderLock lm (lock);
4565 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4569 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4575 operator<< (std::ostream& o, const Meter& m) {
4576 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4580 operator<< (std::ostream& o, const Tempo& t) {
4581 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4585 operator<< (std::ostream& o, const MetricSection& section) {
4587 o << "MetricSection @ " << section.frame() << ' ';
4589 const TempoSection* ts;
4590 const MeterSection* ms;
4592 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4593 o << *((const Tempo*) ts);
4594 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4595 o << *((const Meter*) ms);