2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0, 4.0);
51 MetricSection::frame_at_minute (const double& time) const
53 return (framepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
57 MetricSection::minute_at_frame (const framepos_t& frame) const
59 return (frame / (double) _sample_rate) / 60.0;
62 /***********************************************************************/
65 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
67 /* This is tempo- and meter-sensitive. The number it returns
68 is based on the interval between any two lines in the
69 grid that is constructed from tempo and meter sections.
71 The return value IS NOT interpretable in terms of "beats".
74 return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type/tempo.note_type()));
78 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
80 return frames_per_grid (tempo, sr) * _divisions_per_bar;
83 /***********************************************************************/
85 const string TempoSection::xml_state_node_name = "Tempo";
87 TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
88 : MetricSection (0.0, 0, MusicTime, true, sample_rate)
89 , Tempo (TempoMap::default_tempo())
92 , _locked_to_meter (false)
94 XMLProperty const * prop;
100 _legacy_bbt = BBT_Time (0, 0, 0);
102 if ((prop = node.property ("start")) != 0) {
103 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
107 /* legacy session - start used to be in bbt*/
110 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
114 if ((prop = node.property ("pulse")) != 0) {
115 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
116 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
122 if ((prop = node.property ("frame")) != 0) {
123 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
124 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
125 throw failed_constructor();
127 set_minute (minute_at_frame (frame));
131 /* XX replace old beats-per-minute name with note-types-per-minute */
132 if ((prop = node.property ("beats-per-minute")) != 0) {
133 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
134 error << _("TempoSection XML node has an illegal \"beats-per-minute\" value") << endmsg;
135 throw failed_constructor();
139 if ((prop = node.property ("note-type")) == 0) {
140 /* older session, make note type be quarter by default */
143 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
144 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
145 throw failed_constructor();
149 if ((prop = node.property ("movable")) == 0) {
150 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
151 throw failed_constructor();
154 set_initial (!string_is_affirmative (prop->value()));
156 if ((prop = node.property ("active")) == 0) {
157 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
160 set_active (string_is_affirmative (prop->value()));
163 if ((prop = node.property ("tempo-type")) == 0) {
166 _type = Type (string_2_enum (prop->value(), _type));
169 if ((prop = node.property ("lock-style")) == 0) {
171 set_position_lock_style (MusicTime);
173 set_position_lock_style (AudioTime);
176 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
179 if ((prop = node.property ("locked-to-meter")) == 0) {
180 set_locked_to_meter (false);
182 set_locked_to_meter (string_is_affirmative (prop->value()));
187 TempoSection::get_state() const
189 XMLNode *root = new XMLNode (xml_state_node_name);
193 snprintf (buf, sizeof (buf), "%lf", pulse());
194 root->add_property ("pulse", buf);
195 snprintf (buf, sizeof (buf), "%li", frame());
196 root->add_property ("frame", buf);
197 snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
198 root->add_property ("beats-per-minute", buf);
199 snprintf (buf, sizeof (buf), "%lf", _note_type);
200 root->add_property ("note-type", buf);
201 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
202 root->add_property ("movable", buf);
203 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
204 root->add_property ("active", buf);
205 root->add_property ("tempo-type", enum_2_string (_type));
206 root->add_property ("lock-style", enum_2_string (position_lock_style()));
207 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
213 TempoSection::set_type (Type type)
218 /** returns the Tempo at the session-relative minute.
221 TempoSection::tempo_at_minute (const double& m) const
223 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && m < minute());
225 return Tempo (note_types_per_minute(), note_type());
228 return Tempo (_tempo_at_time (m - minute()), _note_type);
231 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
232 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
233 * @param p the pulse used to calculate the returned minute for constant tempi
234 * @return the minute at the supplied tempo
236 * note that the note_type is currently ignored in this function. see below.
240 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
241 * there should be no ramp between the two even if we are ramped.
242 * in other words a ramp should only place a curve on note_types_per_minute.
243 * we should be able to use Tempo note type here, but the above
244 * complicates things a bit.
247 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
249 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && p < pulse());
251 return ((p - pulse()) / pulses_per_minute()) + minute();
254 return _time_at_tempo (ntpm) + minute();
257 /** returns the Tempo at the supplied whole-note pulse.
260 TempoSection::tempo_at_pulse (const double& p) const
262 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && p < pulse());
265 return Tempo (note_types_per_minute(), note_type());
268 return Tempo (_tempo_at_pulse (p - pulse()), _note_type);
271 /** returns the whole-note pulse where a tempo in note types per minute occurs.
272 * constant tempi require minute m.
273 * @param ntpm the note types per minute value used to calculate the returned pulse
274 * @param m the minute used to calculate the returned pulse if the tempo is constant
275 * @return the whole-note pulse at the supplied tempo
277 * note that note_type is currently ignored in this function. see minute_at_tempo().
279 * for constant tempi, this is anaologous to pulse_at_minute().
282 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
284 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && m < minute());
286 return ((m - minute()) * pulses_per_minute()) + pulse();
289 return _pulse_at_tempo (ntpm) + pulse();
292 /** returns the whole-note pulse at the supplied session-relative minute.
295 TempoSection::pulse_at_minute (const double& m) const
297 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && m < minute());
299 return ((m - minute()) * pulses_per_minute()) + pulse();
302 return _pulse_at_time (m - minute()) + pulse();
305 /** returns the session-relative minute at the supplied whole-note pulse.
308 TempoSection::minute_at_pulse (const double& p) const
310 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && p < pulse());
312 return ((p - pulse()) / pulses_per_minute()) + minute();
315 return _time_at_pulse (p - pulse()) + minute();
318 /** returns thw whole-note pulse at session frame position f.
319 * @param f the frame position.
320 * @return the position in whole-note pulses corresponding to f
322 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
325 TempoSection::pulse_at_frame (const framepos_t& f) const
327 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && f < frame());
329 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
332 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
336 TempoSection::frame_at_pulse (const double& p) const
338 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && p < pulse());
340 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
343 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
351 Tt----|-----------------*|
352 Ta----|--------------|* |
358 _______________|___|____
359 time a t (next tempo)
362 Duration in beats at time a is the integral of some Tempo function.
363 In our case, the Tempo function (Tempo at time t) is
366 with function constant
371 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
372 b(t) = T0(e^(ct) - 1) / c
374 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
375 t(b) = log((c.b / T0) + 1) / c
377 The time t at which Tempo T occurs is a as above:
378 t(T) = log(T / T0) / c
380 The beat at which a Tempo T occurs is:
383 The Tempo at which beat b occurs is:
386 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
387 Our problem is that we usually don't know t.
388 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
389 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
390 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
392 By substituting our expanded t as a in the c function above, our problem is reduced to:
393 c = T0 (e^(log (Ta / T0)) - 1) / b
395 Of course the word 'beat' has been left loosely defined above.
396 In music, a beat is defined by the musical pulse (which comes from the tempo)
397 and the meter in use at a particular time (how many pulse divisions there are in one bar).
398 It would be more accurate to substitute the work 'pulse' for 'beat' above.
402 We can now store c for future time calculations.
403 If the following tempo section (the one that defines c in conjunction with this one)
404 is changed or moved, c is no longer valid.
406 The public methods are session-relative.
408 Most of this stuff is taken from this paper:
411 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
414 Zurich University of Arts
415 Institute for Computer Music and Sound Technology
417 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
421 /** compute this ramp's function constant from some tempo-pulse point
422 * @param end_npm end tempo (in note types per minute)
423 * @param end_pulse duration (pulses into global start) of some other position.
424 * @return the calculated function constant
427 TempoSection::compute_c_func_pulse (const double& end_npm, const double& end_pulse) const
429 if (note_types_per_minute() == end_npm) {
433 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
434 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
437 /** compute the function constant from some tempo-time point.
438 * @param end_npm tempo (note types/min.)
439 * @param end_minute distance (in minutes) from session origin
440 * @return the calculated function constant
443 TempoSection::compute_c_func_minute (const double& end_npm, const double& end_minute) const
445 if (note_types_per_minute() == end_npm) {
449 return c_func (end_npm, end_minute - minute());
452 /* position function */
454 TempoSection::a_func (double end_npm, double c_func) const
456 return log (end_npm / note_types_per_minute()) / c_func;
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_func * 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_func;
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_func) / _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_func) + note_types_per_minute();
494 /* pulse at time in minutes */
496 TempoSection::_pulse_at_time (const double& time) const
498 return (expm1 (_c_func * time) * (note_types_per_minute() / _c_func)) / _note_type;
501 /* time in minutes at pulse */
503 TempoSection::_time_at_pulse (const double& pulse) const
505 return log1p ((_c_func * pulse * _note_type) / note_types_per_minute()) / _c_func;
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 m->set_initial (true);
748 /* note: frame time is correct (zero) for both of these */
750 _metrics.push_back (t);
751 _metrics.push_back (m);
755 TempoMap::TempoMap (TempoMap const & other)
757 _frame_rate = other._frame_rate;
758 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
759 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
760 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
763 TempoSection* new_section = new TempoSection (*ts);
764 _metrics.push_back (new_section);
766 MeterSection* new_section = new MeterSection (*ms);
767 _metrics.push_back (new_section);
773 TempoMap::operator= (TempoMap const & other)
775 if (&other != this) {
776 _frame_rate = other._frame_rate;
778 Metrics::const_iterator d = _metrics.begin();
779 while (d != _metrics.end()) {
785 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
786 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
787 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
790 TempoSection* new_section = new TempoSection (*ts);
791 _metrics.push_back (new_section);
793 MeterSection* new_section = new MeterSection (*ms);
794 _metrics.push_back (new_section);
799 PropertyChanged (PropertyChange());
804 TempoMap::~TempoMap ()
806 Metrics::const_iterator d = _metrics.begin();
807 while (d != _metrics.end()) {
815 TempoMap::frame_at_minute (const double time) const
817 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
821 TempoMap::minute_at_frame (const framepos_t frame) const
823 return (frame / (double) _frame_rate) / 60.0;
827 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
829 bool removed = false;
832 Glib::Threads::RWLock::WriterLock lm (lock);
833 if ((removed = remove_tempo_locked (tempo))) {
834 if (complete_operation) {
835 recompute_map (_metrics);
840 if (removed && complete_operation) {
841 PropertyChanged (PropertyChange ());
846 TempoMap::remove_tempo_locked (const TempoSection& tempo)
850 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
851 if (dynamic_cast<TempoSection*> (*i) != 0) {
852 if (tempo.frame() == (*i)->frame()) {
853 if (!(*i)->initial()) {
866 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
868 bool removed = false;
871 Glib::Threads::RWLock::WriterLock lm (lock);
872 if ((removed = remove_meter_locked (tempo))) {
873 if (complete_operation) {
874 recompute_map (_metrics);
879 if (removed && complete_operation) {
880 PropertyChanged (PropertyChange ());
885 TempoMap::remove_meter_locked (const MeterSection& meter)
888 if (meter.position_lock_style() == AudioTime) {
889 /* remove meter-locked tempo */
890 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
892 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
893 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
902 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
903 if (dynamic_cast<MeterSection*> (*i) != 0) {
904 if (meter.frame() == (*i)->frame()) {
905 if (!(*i)->initial()) {
918 TempoMap::do_insert (MetricSection* section)
920 bool need_add = true;
921 /* we only allow new meters to be inserted on beat 1 of an existing
925 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
927 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
929 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
930 corrected.second.beats = 1;
931 corrected.second.ticks = 0;
932 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
933 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
934 m->bbt(), corrected.second) << endmsg;
935 //m->set_pulse (corrected);
939 /* Look for any existing MetricSection that is of the same type and
940 in the same bar as the new one, and remove it before adding
941 the new one. Note that this means that if we find a matching,
942 existing section, we can break out of the loop since we're
943 guaranteed that there is only one such match.
946 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
948 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
949 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
950 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
951 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
953 if (tempo && insert_tempo) {
956 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
957 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
959 if (tempo->initial()) {
961 /* can't (re)move this section, so overwrite
962 * its data content (but not its properties as
966 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
967 (*i)->set_position_lock_style (AudioTime);
969 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
970 t->set_type (insert_tempo->type());
980 } else if (meter && insert_meter) {
984 bool const ipm = insert_meter->position_lock_style() == MusicTime;
986 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
988 if (meter->initial()) {
990 /* can't (re)move this section, so overwrite
991 * its data content (but not its properties as
995 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
996 (*i)->set_position_lock_style (AudioTime);
1006 /* non-matching types, so we don't care */
1010 /* Add the given MetricSection, if we didn't just reset an existing
1015 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
1016 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
1017 Metrics::iterator i;
1020 TempoSection* prev_t = 0;
1022 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1023 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1024 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1027 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1031 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1035 prev_t = dynamic_cast<TempoSection*> (*i);
1038 } else if (insert_tempo) {
1039 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1040 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1043 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1044 const bool lm = insert_tempo->locked_to_meter();
1045 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1046 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1053 _metrics.insert (i, section);
1057 /* user supplies the exact pulse if pls == MusicTime */
1059 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
1061 if (tempo.note_types_per_minute() <= 0.0) {
1065 TempoSection* ts = 0;
1067 Glib::Threads::RWLock::WriterLock lm (lock);
1068 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
1072 PropertyChanged (PropertyChange ());
1078 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
1080 if (tempo.note_types_per_minute() <= 0.0) {
1084 const bool locked_to_meter = ts.locked_to_meter();
1087 Glib::Threads::RWLock::WriterLock lm (lock);
1088 TempoSection& first (first_tempo());
1089 if (!ts.initial()) {
1090 if (ts.locked_to_meter()) {
1093 /* cannot move a meter-locked tempo section */
1094 *static_cast<Tempo*>(&ts) = tempo;
1095 recompute_map (_metrics);
1098 remove_tempo_locked (ts);
1099 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1102 first.set_type (type);
1103 first.set_pulse (0.0);
1104 first.set_minute (minute_at_frame (frame));
1105 first.set_position_lock_style (AudioTime);
1107 /* cannot move the first tempo section */
1108 *static_cast<Tempo*>(&first) = tempo;
1109 recompute_map (_metrics);
1114 PropertyChanged (PropertyChange ());
1118 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1119 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1121 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1122 t->set_locked_to_meter (locked_to_meter);
1123 bool solved = false;
1128 if (pls == AudioTime) {
1129 solved = solve_map_minute (_metrics, t, t->minute());
1131 solved = solve_map_pulse (_metrics, t, t->pulse());
1133 recompute_meters (_metrics);
1136 if (!solved && recompute) {
1137 recompute_map (_metrics);
1144 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1146 MeterSection* m = 0;
1148 Glib::Threads::RWLock::WriterLock lm (lock);
1149 m = add_meter_locked (meter, beat, where, frame, pls, true);
1154 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1159 PropertyChanged (PropertyChange ());
1164 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1167 Glib::Threads::RWLock::WriterLock lm (lock);
1168 const double beat = beat_at_bbt_locked (_metrics, where);
1170 if (!ms.initial()) {
1171 remove_meter_locked (ms);
1172 add_meter_locked (meter, beat, where, frame, pls, true);
1174 MeterSection& first (first_meter());
1175 TempoSection& first_t (first_tempo());
1176 /* cannot move the first meter section */
1177 *static_cast<Meter*>(&first) = meter;
1178 first.set_position_lock_style (AudioTime);
1179 first.set_pulse (0.0);
1180 //first.set_minute (minute_at_frame (frame));
1181 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1182 first.set_beat (beat);
1183 first_t.set_minute (first.minute());
1184 first_t.set_pulse (0.0);
1185 first_t.set_position_lock_style (AudioTime);
1186 recompute_map (_metrics);
1190 PropertyChanged (PropertyChange ());
1194 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1196 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1197 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1198 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1199 TempoSection* mlt = 0;
1201 if (pls == AudioTime) {
1202 /* add meter-locked tempo */
1203 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), TempoSection::Ramp, AudioTime, true, true);
1211 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1213 bool solved = false;
1215 do_insert (new_meter);
1219 if (pls == AudioTime) {
1220 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1221 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1222 fudge frame so that the meter ends up at its BBT position instead.
1225 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1228 solved = solve_map_bbt (_metrics, new_meter, where);
1229 /* required due to resetting the pulse of meter-locked tempi above.
1230 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1231 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1233 recompute_map (_metrics);
1237 if (!solved && recompute) {
1238 /* if this has failed to solve, there is little we can do other than to ensure that
1239 the new map is recalculated.
1241 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1242 recompute_map (_metrics);
1249 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1251 Tempo newtempo (note_types_per_minute, note_type);
1254 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1255 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1260 Glib::Threads::RWLock::WriterLock lm (lock);
1261 *((Tempo*) t) = newtempo;
1262 recompute_map (_metrics);
1264 PropertyChanged (PropertyChange ());
1271 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1273 Tempo newtempo (note_types_per_minute, note_type);
1276 TempoSection* first;
1277 Metrics::iterator i;
1279 /* find the TempoSection immediately preceding "where"
1282 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1284 if ((*i)->frame() > where) {
1290 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1303 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1313 Glib::Threads::RWLock::WriterLock lm (lock);
1314 /* cannot move the first tempo section */
1315 *((Tempo*)prev) = newtempo;
1316 recompute_map (_metrics);
1319 PropertyChanged (PropertyChange ());
1323 TempoMap::first_meter () const
1325 const MeterSection *m = 0;
1327 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1328 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1333 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1334 abort(); /*NOTREACHED*/
1339 TempoMap::first_meter ()
1341 MeterSection *m = 0;
1343 /* CALLER MUST HOLD LOCK */
1345 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1346 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1351 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1352 abort(); /*NOTREACHED*/
1357 TempoMap::first_tempo () const
1359 const TempoSection *t = 0;
1361 /* CALLER MUST HOLD LOCK */
1363 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1364 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1374 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1375 abort(); /*NOTREACHED*/
1380 TempoMap::first_tempo ()
1382 TempoSection *t = 0;
1384 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1385 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1395 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1396 abort(); /*NOTREACHED*/
1400 TempoMap::recompute_tempi (Metrics& metrics)
1402 TempoSection* prev_t = 0;
1404 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1407 if ((*i)->is_tempo()) {
1408 t = static_cast<TempoSection*> (*i);
1420 if (t->position_lock_style() == AudioTime) {
1421 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
1422 if (!t->locked_to_meter()) {
1423 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1427 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
1428 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1436 prev_t->set_c_func (0.0);
1439 /* tempos must be positioned correctly.
1440 the current approach is to use a meter's bbt time as its base position unit.
1441 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1442 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1445 TempoMap::recompute_meters (Metrics& metrics)
1447 MeterSection* meter = 0;
1448 MeterSection* prev_m = 0;
1450 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1451 if (!(*mi)->is_tempo()) {
1452 meter = static_cast<MeterSection*> (*mi);
1453 if (meter->position_lock_style() == AudioTime) {
1455 pair<double, BBT_Time> b_bbt;
1456 TempoSection* meter_locked_tempo = 0;
1457 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1459 if ((*ii)->is_tempo()) {
1460 t = static_cast<TempoSection*> (*ii);
1461 if ((t->locked_to_meter() || t->initial()) && t->frame() == meter->frame()) {
1462 meter_locked_tempo = t;
1469 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1470 if (beats + prev_m->beat() != meter->beat()) {
1471 /* reordering caused a bbt change */
1473 beats = meter->beat() - prev_m->beat();
1474 b_bbt = make_pair (beats + prev_m->beat()
1475 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1476 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1478 } else if (!meter->initial()) {
1479 b_bbt = make_pair (meter->beat(), meter->bbt());
1480 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1483 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1485 if (meter_locked_tempo) {
1486 meter_locked_tempo->set_pulse (pulse);
1488 meter->set_beat (b_bbt);
1489 meter->set_pulse (pulse);
1494 pair<double, BBT_Time> b_bbt;
1496 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1497 if (beats + prev_m->beat() != meter->beat()) {
1498 /* reordering caused a bbt change */
1499 b_bbt = make_pair (beats + prev_m->beat()
1500 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1502 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1504 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1506 /* shouldn't happen - the first is audio-locked */
1507 pulse = pulse_at_beat_locked (metrics, meter->beat());
1508 b_bbt = make_pair (meter->beat(), meter->bbt());
1511 meter->set_beat (b_bbt);
1512 meter->set_pulse (pulse);
1513 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1522 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1524 /* CALLER MUST HOLD WRITE LOCK */
1526 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1529 /* silly call from Session::process() during startup
1534 recompute_tempi (metrics);
1535 recompute_meters (metrics);
1539 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1541 Glib::Threads::RWLock::ReaderLock lm (lock);
1542 TempoMetric m (first_meter(), first_tempo());
1544 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1545 at something, because we insert the default tempo and meter during
1546 TempoMap construction.
1548 now see if we can find better candidates.
1551 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1553 if ((*i)->frame() > frame) {
1567 /* XX meters only */
1569 TempoMap::metric_at (BBT_Time bbt) const
1571 Glib::Threads::RWLock::ReaderLock lm (lock);
1572 TempoMetric m (first_meter(), first_tempo());
1574 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1575 at something, because we insert the default tempo and meter during
1576 TempoMap construction.
1578 now see if we can find better candidates.
1581 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1583 if (!(*i)->is_tempo()) {
1584 mw = static_cast<MeterSection*> (*i);
1585 BBT_Time section_start (mw->bbt());
1587 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1598 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1599 * @param frame The session frame position.
1600 * @return The beat duration according to the tempo map at the supplied frame.
1602 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1603 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1605 * This function uses both tempo and meter.
1608 TempoMap::beat_at_frame (const framecnt_t& frame) const
1610 Glib::Threads::RWLock::ReaderLock lm (lock);
1612 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1615 /* This function uses both tempo and meter.*/
1617 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1619 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1620 MeterSection* prev_m = 0;
1621 MeterSection* next_m = 0;
1623 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1624 if (!(*i)->is_tempo()) {
1625 if (prev_m && (*i)->minute() > minute) {
1626 next_m = static_cast<MeterSection*> (*i);
1629 prev_m = static_cast<MeterSection*> (*i);
1633 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1635 /* audio locked meters fake their beat */
1636 if (next_m && next_m->beat() < beat) {
1637 return next_m->beat();
1643 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1644 * @param beat The BBT (meter-based) beat.
1645 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1647 * This function uses both tempo and meter.
1650 TempoMap::frame_at_beat (const double& beat) const
1652 Glib::Threads::RWLock::ReaderLock lm (lock);
1654 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1657 /* meter & tempo section based */
1659 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1661 MeterSection* prev_m = 0;
1662 TempoSection* prev_t = 0;
1666 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1667 if (!(*i)->is_tempo()) {
1668 m = static_cast<MeterSection*> (*i);
1669 if (prev_m && m->beat() > beat) {
1679 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1680 if ((*i)->is_tempo()) {
1681 t = static_cast<TempoSection*> (*i);
1682 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1691 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1694 /** Returns a Tempo corresponding to the supplied frame position.
1695 * @param frame The audio frame.
1696 * @return a Tempo according to the tempo map at the supplied frame.
1700 TempoMap::tempo_at_frame (const framepos_t& frame) const
1702 Glib::Threads::RWLock::ReaderLock lm (lock);
1704 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1708 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1710 TempoSection* prev_t = 0;
1714 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1715 if ((*i)->is_tempo()) {
1716 t = static_cast<TempoSection*> (*i);
1720 if ((prev_t) && t->minute() > minute) {
1721 /* t is the section past frame */
1722 return prev_t->tempo_at_minute (minute);
1728 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1731 /** returns the frame at which the supplied tempo occurs, or
1732 * the frame of the last tempo section (search exhausted)
1733 * only the position of the first occurence will be returned
1737 TempoMap::frame_at_tempo (const Tempo& tempo) const
1739 Glib::Threads::RWLock::ReaderLock lm (lock);
1741 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1745 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1747 TempoSection* prev_t = 0;
1748 const double tempo_bpm = tempo.note_types_per_minute();
1750 Metrics::const_iterator i;
1752 for (i = metrics.begin(); i != metrics.end(); ++i) {
1754 if ((*i)->is_tempo()) {
1755 t = static_cast<TempoSection*> (*i);
1761 const double t_bpm = t->note_types_per_minute();
1763 if (t_bpm == tempo_bpm) {
1768 const double prev_t_bpm = prev_t->note_types_per_minute();
1770 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1771 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1778 return prev_t->minute();
1782 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1784 TempoSection* prev_t = 0;
1788 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1789 if ((*i)->is_tempo()) {
1790 t = static_cast<TempoSection*> (*i);
1794 if ((prev_t) && t->pulse() > pulse) {
1795 /* t is the section past frame */
1796 return prev_t->tempo_at_pulse (pulse);
1802 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1806 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1808 TempoSection* prev_t = 0;
1809 const double tempo_bpm = tempo.note_types_per_minute();
1811 Metrics::const_iterator i;
1813 for (i = metrics.begin(); i != metrics.end(); ++i) {
1815 if ((*i)->is_tempo()) {
1816 t = static_cast<TempoSection*> (*i);
1822 const double t_bpm = t->note_types_per_minute();
1824 if (t_bpm == tempo_bpm) {
1829 const double prev_t_bpm = prev_t->note_types_per_minute();
1831 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1832 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1839 return prev_t->pulse();
1842 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1843 * @param qn the position in quarter note beats.
1844 * @return the Tempo at the supplied quarter-note.
1847 TempoMap::tempo_at_quarter_note (const double& qn) const
1849 Glib::Threads::RWLock::ReaderLock lm (lock);
1851 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1854 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1855 * @param tempo the tempo.
1856 * @return the position in quarter-note beats where the map bpm
1857 * is equal to that of the Tempo. currently ignores note_type.
1860 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1862 Glib::Threads::RWLock::ReaderLock lm (lock);
1864 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1867 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1868 * @param metrics the list of metric sections used to calculate the pulse.
1869 * @param beat The BBT (meter-based) beat.
1870 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1872 * a pulse or whole note is the base musical position of a MetricSection.
1873 * it is equivalent to four quarter notes.
1877 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1879 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1881 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1884 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1885 * @param metrics the list of metric sections used to calculate the beat.
1886 * @param pulse the whole-note pulse.
1887 * @return the meter-based beat at the supplied whole-note pulse.
1889 * a pulse or whole note is the base musical position of a MetricSection.
1890 * it is equivalent to four quarter notes.
1893 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1895 MeterSection* prev_m = 0;
1897 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1899 if (!(*i)->is_tempo()) {
1900 m = static_cast<MeterSection*> (*i);
1901 if (prev_m && m->pulse() > pulse) {
1909 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1913 /* tempo section based */
1915 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1917 /* HOLD (at least) THE READER LOCK */
1918 TempoSection* prev_t = 0;
1920 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1922 if ((*i)->is_tempo()) {
1923 t = static_cast<TempoSection*> (*i);
1927 if (prev_t && t->minute() > minute) {
1928 /*the previous ts is the one containing the frame */
1929 const double ret = prev_t->pulse_at_minute (minute);
1930 /* audio locked section in new meter*/
1931 if (t->pulse() < ret) {
1940 /* treated as constant for this ts */
1941 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1943 return pulses_in_section + prev_t->pulse();
1946 /* tempo section based */
1948 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1950 /* HOLD THE READER LOCK */
1952 const TempoSection* prev_t = 0;
1954 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1957 if ((*i)->is_tempo()) {
1958 t = static_cast<TempoSection*> (*i);
1962 if (prev_t && t->pulse() > pulse) {
1963 return prev_t->minute_at_pulse (pulse);
1969 /* must be treated as constant, irrespective of _type */
1970 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1972 return dtime + prev_t->minute();
1975 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1976 * @param bbt The BBT time (meter-based).
1977 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1981 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1983 Glib::Threads::RWLock::ReaderLock lm (lock);
1984 return beat_at_bbt_locked (_metrics, bbt);
1989 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1991 /* CALLER HOLDS READ LOCK */
1993 MeterSection* prev_m = 0;
1995 /* because audio-locked meters have 'fake' integral beats,
1996 there is no pulse offset here.
2000 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2001 if (!(*i)->is_tempo()) {
2002 m = static_cast<MeterSection*> (*i);
2004 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2005 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2013 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2014 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2015 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2020 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2021 * @param beat The BBT (meter-based) beat.
2022 * @return The BBT time (meter-based) at the supplied meter-based beat.
2026 TempoMap::bbt_at_beat (const double& beat)
2028 Glib::Threads::RWLock::ReaderLock lm (lock);
2029 return bbt_at_beat_locked (_metrics, beat);
2033 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2035 /* CALLER HOLDS READ LOCK */
2036 MeterSection* prev_m = 0;
2037 const double beats = max (0.0, b);
2039 MeterSection* m = 0;
2041 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2042 if (!(*i)->is_tempo()) {
2043 m = static_cast<MeterSection*> (*i);
2045 if (m->beat() > beats) {
2046 /* this is the meter after the one our beat is on*/
2056 const double beats_in_ms = beats - prev_m->beat();
2057 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2058 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2059 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2060 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2064 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2065 ret.beats = (uint32_t) floor (remaining_beats);
2066 ret.bars = total_bars;
2068 /* 0 0 0 to 1 1 0 - based mapping*/
2072 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2074 ret.ticks -= BBT_Time::ticks_per_beat;
2077 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2085 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2086 * @param bbt The BBT time (meter-based).
2087 * @return the quarter note beat at the supplied BBT time
2089 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2091 * while the input uses meter, the output does not.
2094 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2096 Glib::Threads::RWLock::ReaderLock lm (lock);
2098 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2102 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2104 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2107 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2110 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2114 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2116 /* CALLER HOLDS READ LOCK */
2118 MeterSection* prev_m = 0;
2120 /* because audio-locked meters have 'fake' integral beats,
2121 there is no pulse offset here.
2125 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2126 if (!(*i)->is_tempo()) {
2127 m = static_cast<MeterSection*> (*i);
2129 if (m->bbt().bars > bbt.bars) {
2137 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2138 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2139 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2144 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2145 * @param qn the quarter-note beat.
2146 * @return The BBT time (meter-based) at the supplied meter-based beat.
2148 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2152 TempoMap::bbt_at_quarter_note (const double& qn)
2154 Glib::Threads::RWLock::ReaderLock lm (lock);
2156 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2159 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2160 * @param metrics The list of metric sections used to determine the result.
2161 * @param pulse The whole-note pulse.
2162 * @return The BBT time at the supplied whole-note pulse.
2164 * a pulse or whole note is the basic musical position of a MetricSection.
2165 * it is equivalent to four quarter notes.
2166 * while the output uses meter, the input does not.
2169 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2171 MeterSection* prev_m = 0;
2173 MeterSection* m = 0;
2175 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2177 if (!(*i)->is_tempo()) {
2178 m = static_cast<MeterSection*> (*i);
2181 double const pulses_to_m = m->pulse() - prev_m->pulse();
2182 if (prev_m->pulse() + pulses_to_m > pulse) {
2183 /* this is the meter after the one our beat is on*/
2194 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2195 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2196 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2197 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2198 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2202 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2203 ret.beats = (uint32_t) floor (remaining_beats);
2204 ret.bars = total_bars;
2206 /* 0 0 0 to 1 1 0 mapping*/
2210 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2212 ret.ticks -= BBT_Time::ticks_per_beat;
2215 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2223 /** Returns the BBT time corresponding to the supplied frame position.
2224 * @param frame the position in audio samples.
2225 * @return the BBT time at the frame position .
2229 TempoMap::bbt_at_frame (framepos_t frame)
2236 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2239 Glib::Threads::RWLock::ReaderLock lm (lock);
2241 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2245 TempoMap::bbt_at_frame_rt (framepos_t frame)
2247 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2250 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2253 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2257 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2267 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2268 MeterSection* prev_m = 0;
2269 MeterSection* next_m = 0;
2273 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2274 if (!(*i)->is_tempo()) {
2275 m = static_cast<MeterSection*> (*i);
2276 if (prev_m && m->minute() > minute) {
2284 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2286 /* handle frame before first meter */
2287 if (minute < prev_m->minute()) {
2290 /* audio locked meters fake their beat */
2291 if (next_m && next_m->beat() < beat) {
2292 beat = next_m->beat();
2295 beat = max (0.0, beat);
2297 const double beats_in_ms = beat - prev_m->beat();
2298 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2299 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2300 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2301 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2305 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2306 ret.beats = (uint32_t) floor (remaining_beats);
2307 ret.bars = total_bars;
2309 /* 0 0 0 to 1 1 0 - based mapping*/
2313 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2315 ret.ticks -= BBT_Time::ticks_per_beat;
2318 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2326 /** Returns the frame position corresponding to the supplied BBT time.
2327 * @param bbt the position in BBT time.
2328 * @return the frame position at bbt.
2332 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2335 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2339 if (bbt.beats < 1) {
2340 throw std::logic_error ("beats are counted from one");
2342 Glib::Threads::RWLock::ReaderLock lm (lock);
2344 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2347 /* meter & tempo section based */
2349 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2351 /* HOLD THE READER LOCK */
2353 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2358 * Returns the quarter-note beat position corresponding to the supplied frame.
2360 * @param frame the position in frames.
2361 * @return The quarter-note position of the supplied frame. Ignores meter.
2365 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2367 Glib::Threads::RWLock::ReaderLock lm (lock);
2369 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2375 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2377 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2383 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2385 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2388 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2391 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2397 * Returns the frame position corresponding to the supplied quarter-note beat.
2399 * @param quarter_note the quarter-note position.
2400 * @return the frame position of the supplied quarter-note. Ignores meter.
2405 TempoMap::frame_at_quarter_note (const double quarter_note) const
2407 Glib::Threads::RWLock::ReaderLock lm (lock);
2409 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2415 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2417 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2422 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2423 * @param beat The BBT (meter-based) beat.
2424 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2426 * a quarter-note may be compared with and assigned to Evoral::Beats.
2430 TempoMap::quarter_note_at_beat (const double beat) const
2432 Glib::Threads::RWLock::ReaderLock lm (lock);
2434 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2440 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2442 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2447 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2448 * @param quarter_note The position in quarter-note beats.
2449 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2451 * a quarter-note is the musical unit of Evoral::Beats.
2455 TempoMap::beat_at_quarter_note (const double quarter_note) const
2457 Glib::Threads::RWLock::ReaderLock lm (lock);
2459 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2465 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2468 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2471 /** Returns the duration in frames between two supplied quarter-note beat positions.
2472 * @param start the first position in quarter-note beats.
2473 * @param end the end position in quarter-note beats.
2474 * @return the frame distance ober the quarter-note beats duration.
2476 * use this rather than e.g.
2477 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2478 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2482 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2484 Glib::Threads::RWLock::ReaderLock lm (lock);
2486 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2490 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2493 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2497 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2499 Glib::Threads::RWLock::ReaderLock lm (lock);
2501 return quarter_notes_between_frames_locked (_metrics, start, end);
2505 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2507 const TempoSection* prev_t = 0;
2509 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2512 if ((*i)->is_tempo()) {
2513 t = static_cast<TempoSection*> (*i);
2517 if (prev_t && t->frame() > start) {
2524 const double start_qn = prev_t->pulse_at_frame (start);
2526 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2529 if ((*i)->is_tempo()) {
2530 t = static_cast<TempoSection*> (*i);
2534 if (prev_t && t->frame() > end) {
2540 const double end_qn = prev_t->pulse_at_frame (end);
2542 return (end_qn - start_qn) * 4.0;
2546 TempoMap::check_solved (const Metrics& metrics) const
2548 TempoSection* prev_t = 0;
2549 MeterSection* prev_m = 0;
2551 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2554 if ((*i)->is_tempo()) {
2555 t = static_cast<TempoSection*> (*i);
2560 /* check ordering */
2561 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2565 /* precision check ensures tempo and frames align.*/
2566 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2567 if (!t->locked_to_meter()) {
2572 /* gradient limit - who knows what it should be?
2573 things are also ok (if a little chaotic) without this
2575 if (fabs (prev_t->c_func()) > 1000.0) {
2576 //std::cout << "c : " << prev_t->c_func() << std::endl;
2583 if (!(*i)->is_tempo()) {
2584 m = static_cast<MeterSection*> (*i);
2585 if (prev_m && m->position_lock_style() == AudioTime) {
2586 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2587 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2588 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2590 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2604 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2606 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2608 if ((*i)->is_tempo()) {
2609 t = static_cast<TempoSection*> (*i);
2611 t->set_active (true);
2613 } else if (t->position_lock_style() == AudioTime) {
2614 if (t->active () && t->frame() < frame) {
2615 t->set_active (false);
2617 } else if (t->frame() > frame) {
2618 t->set_active (true);
2619 } else if (t->frame() == frame) {
2629 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2631 TempoSection* prev_t = 0;
2632 TempoSection* section_prev = 0;
2633 double first_m_minute = 0.0;
2634 const bool sml = section->locked_to_meter();
2636 /* can't move a tempo before the first meter */
2637 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2639 if (!(*i)->is_tempo()) {
2640 m = static_cast<MeterSection*> (*i);
2642 first_m_minute = m->minute();
2647 if (!section->initial() && minute <= first_m_minute) {
2651 section->set_active (true);
2652 section->set_minute (minute);
2654 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2656 if ((*i)->is_tempo()) {
2657 t = static_cast<TempoSection*> (*i);
2669 if (t->frame() == frame_at_minute (minute)) {
2673 const bool tlm = t->position_lock_style() == MusicTime;
2675 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2676 section_prev = prev_t;
2678 section_prev->set_c_func (section_prev->compute_c_func_minute (section->note_types_per_minute(), minute));
2679 if (!section->locked_to_meter()) {
2680 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2685 if (t->position_lock_style() == MusicTime) {
2686 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2687 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2689 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2690 if (!t->locked_to_meter()) {
2691 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2700 recompute_tempi (imaginary);
2702 if (check_solved (imaginary)) {
2705 dunp (imaginary, std::cout);
2709 MetricSectionFrameSorter fcmp;
2710 imaginary.sort (fcmp);
2712 recompute_tempi (imaginary);
2714 if (check_solved (imaginary)) {
2722 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2724 TempoSection* prev_t = 0;
2725 TempoSection* section_prev = 0;
2727 section->set_pulse (pulse);
2729 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2731 if ((*i)->is_tempo()) {
2732 t = static_cast<TempoSection*> (*i);
2743 section_prev = prev_t;
2746 if (t->position_lock_style() == MusicTime) {
2747 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2748 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2750 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2751 if (!t->locked_to_meter()) {
2752 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2761 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->note_types_per_minute(), pulse));
2762 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2766 recompute_tempi (imaginary);
2768 if (check_solved (imaginary)) {
2771 dunp (imaginary, std::cout);
2775 MetricSectionSorter cmp;
2776 imaginary.sort (cmp);
2778 recompute_tempi (imaginary);
2780 * XX need a restriction here, but only for this case,
2781 * as audio locked tempos don't interact in the same way.
2783 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2785 * |50 bpm |250 bpm |60 bpm
2786 * drag 250 to the pulse after 60->
2787 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2789 if (check_solved (imaginary)) {
2797 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2799 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2800 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2801 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2805 if (section->initial()) {
2806 /* lock the first tempo to our first meter */
2807 if (!set_active_tempi (imaginary, section->frame_at_minute (minute))) {
2812 TempoSection* meter_locked_tempo = 0;
2814 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2816 if ((*ii)->is_tempo()) {
2817 t = static_cast<TempoSection*> (*ii);
2818 if ((t->locked_to_meter() || t->initial()) && t->frame() == section->frame()) {
2819 meter_locked_tempo = t;
2825 if (!meter_locked_tempo) {
2829 MeterSection* prev_m = 0;
2831 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2832 bool solved = false;
2834 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2836 if (!(*i)->is_tempo()) {
2837 m = static_cast<MeterSection*> (*i);
2839 if (prev_m && !section->initial()) {
2840 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2841 if (beats + prev_m->beat() < section->beat()) {
2842 /* set the section pulse according to its musical position,
2843 * as an earlier time than this has been requested.
2845 const double new_pulse = ((section->beat() - prev_m->beat())
2846 / prev_m->note_divisor()) + prev_m->pulse();
2848 tempo_copy->set_position_lock_style (MusicTime);
2849 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2850 meter_locked_tempo->set_position_lock_style (MusicTime);
2851 section->set_position_lock_style (MusicTime);
2852 section->set_pulse (new_pulse);
2853 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2854 meter_locked_tempo->set_position_lock_style (AudioTime);
2855 section->set_position_lock_style (AudioTime);
2856 section->set_minute (meter_locked_tempo->minute());
2862 Metrics::const_iterator d = future_map.begin();
2863 while (d != future_map.end()) {
2872 /* all is ok. set section's locked tempo if allowed.
2873 possibly disallow if there is an adjacent audio-locked tempo.
2874 XX this check could possibly go. its never actually happened here.
2876 MeterSection* meter_copy = const_cast<MeterSection*>
2877 (&meter_section_at_minute_locked (future_map, section->minute()));
2879 meter_copy->set_minute (minute);
2881 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2882 section->set_minute (minute);
2883 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2884 / prev_m->note_divisor()) + prev_m->pulse());
2885 solve_map_minute (imaginary, meter_locked_tempo, minute);
2890 Metrics::const_iterator d = future_map.begin();
2891 while (d != future_map.end()) {
2901 /* initial (first meter atm) */
2903 tempo_copy->set_minute (minute);
2904 tempo_copy->set_pulse (0.0);
2906 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2907 section->set_minute (minute);
2908 meter_locked_tempo->set_minute (minute);
2909 meter_locked_tempo->set_pulse (0.0);
2910 solve_map_minute (imaginary, meter_locked_tempo, minute);
2915 Metrics::const_iterator d = future_map.begin();
2916 while (d != future_map.end()) {
2925 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2926 section->set_beat (b_bbt);
2927 section->set_pulse (0.0);
2937 MetricSectionFrameSorter fcmp;
2938 imaginary.sort (fcmp);
2940 recompute_meters (imaginary);
2946 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2948 /* disallow setting section to an existing meter's bbt */
2949 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2951 if (!(*i)->is_tempo()) {
2952 m = static_cast<MeterSection*> (*i);
2953 if (m != section && m->bbt().bars == when.bars) {
2959 MeterSection* prev_m = 0;
2960 MeterSection* section_prev = 0;
2962 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2964 if (!(*i)->is_tempo()) {
2965 m = static_cast<MeterSection*> (*i);
2971 pair<double, BBT_Time> b_bbt;
2972 double new_pulse = 0.0;
2974 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2975 section_prev = prev_m;
2977 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2978 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2979 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2981 section->set_beat (b_bbt);
2982 section->set_pulse (pulse);
2983 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2987 if (m->position_lock_style() == AudioTime) {
2988 TempoSection* meter_locked_tempo = 0;
2990 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2992 if ((*ii)->is_tempo()) {
2993 t = static_cast<TempoSection*> (*ii);
2994 if ((t->locked_to_meter() || t->initial()) && t->frame() == m->frame()) {
2995 meter_locked_tempo = t;
3001 if (!meter_locked_tempo) {
3006 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3008 if (beats + prev_m->beat() != m->beat()) {
3009 /* tempo/ meter change caused a change in beat (bar). */
3011 /* the user has requested that the previous section of music overlaps this one.
3012 we have no choice but to change the bar number here, as being locked to audio means
3013 we must stay where we are on the timeline.
3015 beats = m->beat() - prev_m->beat();
3016 b_bbt = make_pair (beats + prev_m->beat()
3017 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3018 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3020 } else if (!m->initial()) {
3021 b_bbt = make_pair (m->beat(), m->bbt());
3022 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3025 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3028 meter_locked_tempo->set_pulse (new_pulse);
3029 m->set_beat (b_bbt);
3030 m->set_pulse (new_pulse);
3034 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3035 if (beats + prev_m->beat() != m->beat()) {
3036 /* tempo/ meter change caused a change in beat (bar). */
3037 b_bbt = make_pair (beats + prev_m->beat()
3038 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3040 b_bbt = make_pair (beats + prev_m->beat()
3043 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3044 m->set_beat (b_bbt);
3045 m->set_pulse (new_pulse);
3046 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3053 if (!section_prev) {
3055 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3056 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3057 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3059 section->set_beat (b_bbt);
3060 section->set_pulse (pulse);
3061 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3064 MetricSectionSorter cmp;
3065 imaginary.sort (cmp);
3067 recompute_meters (imaginary);
3072 /** places a copy of _metrics into copy and returns a pointer
3073 * to section's equivalent in copy.
3076 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3078 TempoSection* ret = 0;
3080 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3083 if ((*i)->is_tempo()) {
3084 t = static_cast<TempoSection*> (*i);
3086 ret = new TempoSection (*t);
3087 copy.push_back (ret);
3091 TempoSection* cp = new TempoSection (*t);
3092 copy.push_back (cp);
3094 if (!(*i)->is_tempo()) {
3095 m = static_cast<MeterSection *> (*i);
3096 MeterSection* cp = new MeterSection (*m);
3097 copy.push_back (cp);
3105 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3107 MeterSection* ret = 0;
3109 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3112 if ((*i)->is_tempo()) {
3113 t = static_cast<TempoSection*> (*i);
3114 TempoSection* cp = new TempoSection (*t);
3115 copy.push_back (cp);
3118 if (!(*i)->is_tempo()) {
3119 m = static_cast<MeterSection *> (*i);
3121 ret = new MeterSection (*m);
3122 copy.push_back (ret);
3125 MeterSection* cp = new MeterSection (*m);
3126 copy.push_back (cp);
3133 /** answers the question "is this a valid beat position for this tempo section?".
3134 * it returns true if the tempo section can be moved to the requested bbt position,
3135 * leaving the tempo map in a solved state.
3136 * @param ts the tempo section to be moved
3137 * @param bbt the requested new position for the tempo section
3138 * @return true if the tempo section can be moved to the position, otherwise false.
3141 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3144 TempoSection* tempo_copy = 0;
3147 Glib::Threads::RWLock::ReaderLock lm (lock);
3148 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3154 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3156 Metrics::const_iterator d = copy.begin();
3157 while (d != copy.end()) {
3166 * 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,
3167 * taking any possible reordering as a consequence of this into account.
3168 * @param section - the section to be altered
3169 * @param bbt - the BBT time where the altered tempo will fall
3170 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3172 pair<double, framepos_t>
3173 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3176 pair<double, framepos_t> ret = make_pair (0.0, 0);
3178 Glib::Threads::RWLock::ReaderLock lm (lock);
3180 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3182 const double beat = beat_at_bbt_locked (future_map, bbt);
3184 if (section->position_lock_style() == AudioTime) {
3185 tempo_copy->set_position_lock_style (MusicTime);
3188 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3189 ret.first = tempo_copy->pulse();
3190 ret.second = tempo_copy->frame();
3192 ret.first = section->pulse();
3193 ret.second = section->frame();
3196 Metrics::const_iterator d = future_map.begin();
3197 while (d != future_map.end()) {
3204 /** moves a TempoSection to a specified position.
3205 * @param ts - the section to be moved
3206 * @param frame - the new position in frames for the tempo
3207 * @param sub_num - the snap division to use if using musical time.
3209 * if sub_num is non-zero, the frame position is used to calculate an exact
3212 * -1 | snap to bars (meter-based)
3213 * 0 | no snap - use audio frame for musical position
3214 * 1 | snap to meter-based (BBT) beat
3215 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3217 * this follows the snap convention in the gui.
3218 * if sub_num is zero, the musical position will be taken from the supplied frame.
3221 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3225 if (ts->position_lock_style() == MusicTime) {
3227 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3228 Glib::Threads::RWLock::WriterLock lm (lock);
3229 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3231 tempo_copy->set_position_lock_style (AudioTime);
3233 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3234 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3235 const double pulse = pulse_at_beat_locked (future_map, beat);
3237 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3238 solve_map_pulse (_metrics, ts, pulse);
3239 recompute_meters (_metrics);
3247 Glib::Threads::RWLock::WriterLock lm (lock);
3248 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3250 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3252 /* We're moving the object that defines the grid while snapping to it...
3253 * Placing the ts at the beat corresponding to the requested frame may shift the
3254 * grid in such a way that the mouse is left hovering over a completerly different division,
3255 * causing jittering when the mouse next moves (esp. large tempo deltas).
3257 * This alters the snap behaviour slightly in that we snap to beat divisions
3258 * in the future map rather than the existing one.
3260 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3261 const framepos_t snapped_frame = frame_at_minute (minute_at_quarter_note_locked (future_map, qn));
3263 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3264 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3265 ts->set_pulse (qn / 4.0);
3266 recompute_meters (_metrics);
3269 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3270 recompute_meters (_metrics);
3276 Metrics::const_iterator d = future_map.begin();
3277 while (d != future_map.end()) {
3282 MetricPositionChanged (); // Emit Signal
3285 /** moves a MeterSection to a specified position.
3286 * @param ms - the section to be moved
3287 * @param frame - the new position in frames for the meter
3289 * as a meter cannot snap to anything but bars,
3290 * the supplied frame is rounded to the nearest bar, possibly
3291 * leaving the meter position unchanged.
3294 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3298 if (ms->position_lock_style() == AudioTime) {
3301 Glib::Threads::RWLock::WriterLock lm (lock);
3302 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3304 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3305 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3306 recompute_tempi (_metrics);
3311 Glib::Threads::RWLock::WriterLock lm (lock);
3312 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3314 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3315 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3317 if (solve_map_bbt (future_map, copy, bbt)) {
3318 solve_map_bbt (_metrics, ms, bbt);
3319 recompute_tempi (_metrics);
3324 Metrics::const_iterator d = future_map.begin();
3325 while (d != future_map.end()) {
3330 MetricPositionChanged (); // Emit Signal
3334 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3337 bool can_solve = false;
3339 Glib::Threads::RWLock::WriterLock lm (lock);
3340 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3341 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3342 recompute_tempi (future_map);
3344 if (check_solved (future_map)) {
3345 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3346 recompute_map (_metrics);
3351 Metrics::const_iterator d = future_map.begin();
3352 while (d != future_map.end()) {
3357 MetricPositionChanged (); // Emit Signal
3363 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3366 Ts (future prev_t) Tnext
3369 |----------|----------
3376 Glib::Threads::RWLock::WriterLock lm (lock);
3382 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3383 TempoSection* prev_to_prev_t = 0;
3384 const frameoffset_t fr_off = end_frame - frame;
3388 if (prev_t->pulse() > 0.0) {
3389 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3392 TempoSection* next_t = 0;
3393 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3394 TempoSection* t = 0;
3395 if ((*i)->is_tempo()) {
3396 t = static_cast<TempoSection*> (*i);
3397 if (t->frame() > ts->frame()) {
3403 /* minimum allowed measurement distance in frames */
3404 const framepos_t min_dframe = 2;
3406 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3407 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3409 double contribution = 0.0;
3411 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3412 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3415 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3417 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3418 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3422 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3424 if (prev_t->position_lock_style() == MusicTime) {
3425 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3426 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3428 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3429 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3431 new_bpm = prev_t->note_types_per_minute();
3434 /* prev to prev is irrelevant */
3436 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3437 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3439 new_bpm = prev_t->note_types_per_minute();
3444 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3445 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3447 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3448 / (double) ((end_frame) - prev_to_prev_t->frame()));
3450 new_bpm = prev_t->note_types_per_minute();
3453 /* prev_to_prev_t is irrelevant */
3455 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3456 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3458 new_bpm = prev_t->note_types_per_minute();
3464 double frame_ratio = 1.0;
3465 double pulse_ratio = 1.0;
3466 const double pulse_pos = frame;
3468 if (prev_to_prev_t) {
3469 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3470 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3472 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3473 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3476 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3477 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3479 pulse_ratio = (start_pulse / end_pulse);
3481 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3484 /* don't clamp and proceed here.
3485 testing has revealed that this can go negative,
3486 which is an entirely different thing to just being too low.
3488 if (new_bpm < 0.5) {
3491 new_bpm = min (new_bpm, (double) 1000.0);
3492 prev_t->set_note_types_per_minute (new_bpm);
3493 recompute_tempi (future_map);
3494 recompute_meters (future_map);
3496 if (check_solved (future_map)) {
3497 ts->set_note_types_per_minute (new_bpm);
3498 recompute_tempi (_metrics);
3499 recompute_meters (_metrics);
3503 Metrics::const_iterator d = future_map.begin();
3504 while (d != future_map.end()) {
3509 MetricPositionChanged (); // Emit Signal
3512 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3513 * the supplied frame, possibly returning a negative value.
3515 * @param frame The session frame position.
3516 * @param sub_num The subdivision to use when rounding the beat.
3517 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3518 * Positive integers indicate quarter note (non BBT) divisions.
3519 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3520 * @return The beat position of the supplied frame.
3522 * when working to a musical grid, the use of sub_nom indicates that
3523 * the position should be interpreted musically.
3525 * it effectively snaps to meter bars, meter beats or quarter note divisions
3526 * (as per current gui convention) and returns a musical position independent of frame rate.
3528 * If the supplied frame lies before the first meter, the return will be negative,
3529 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3530 * the continuation of the tempo curve (backwards).
3532 * This function is sensitive to tempo and meter.
3535 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3537 Glib::Threads::RWLock::ReaderLock lm (lock);
3539 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3543 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3545 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3548 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3549 * the supplied frame, possibly returning a negative value.
3551 * @param frame The session frame position.
3552 * @param sub_num The subdivision to use when rounding the quarter note.
3553 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3554 * Positive integers indicate quarter note (non BBT) divisions.
3555 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3556 * @return The quarter note position of the supplied frame.
3558 * When working to a musical grid, the use of sub_nom indicates that
3559 * the frame position should be interpreted musically.
3561 * it effectively snaps to meter bars, meter beats or quarter note divisions
3562 * (as per current gui convention) and returns a musical position independent of frame rate.
3564 * If the supplied frame lies before the first meter, the return will be negative,
3565 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3566 * the continuation of the tempo curve (backwards).
3568 * This function is tempo-sensitive.
3571 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3573 Glib::Threads::RWLock::ReaderLock lm (lock);
3575 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3579 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3581 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3584 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3585 } else if (sub_num == 1) {
3586 /* the gui requested exact musical (BBT) beat */
3587 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3588 } else if (sub_num == -1) {
3590 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3594 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3596 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3598 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3608 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3609 * @param pos the frame position in the tempo map.
3610 * @param bbt the distance in BBT time from pos to calculate.
3611 * @param dir the rounding direction..
3612 * @return the duration in frames between pos and bbt
3615 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3617 Glib::Threads::RWLock::ReaderLock lm (lock);
3619 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3621 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3624 pos_bbt.bars += bbt.bars;
3626 pos_bbt.ticks += bbt.ticks;
3627 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3629 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3632 pos_bbt.beats += bbt.beats;
3633 if ((double) pos_bbt.beats > divisions) {
3635 pos_bbt.beats -= divisions;
3637 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3639 return pos_bbt_frame - pos;
3643 if (pos_bbt.bars <= bbt.bars) {
3646 pos_bbt.bars -= bbt.bars;
3649 if (pos_bbt.ticks < bbt.ticks) {
3650 if (pos_bbt.bars > 1) {
3651 if (pos_bbt.beats == 1) {
3653 pos_bbt.beats = divisions;
3657 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3663 pos_bbt.ticks -= bbt.ticks;
3666 if (pos_bbt.beats <= bbt.beats) {
3667 if (pos_bbt.bars > 1) {
3669 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3674 pos_bbt.beats -= bbt.beats;
3677 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3684 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3686 return round_to_type (fr, dir, Bar);
3690 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3692 return round_to_type (fr, dir, Beat);
3696 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3698 Glib::Threads::RWLock::ReaderLock lm (lock);
3699 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3700 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3701 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3703 ticks -= beats * BBT_Time::ticks_per_beat;
3706 /* round to next (or same iff dir == RoundUpMaybe) */
3708 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3710 if (mod == 0 && dir == RoundUpMaybe) {
3711 /* right on the subdivision, which is fine, so do nothing */
3713 } else if (mod == 0) {
3714 /* right on the subdivision, so the difference is just the subdivision ticks */
3715 ticks += ticks_one_subdivisions_worth;
3718 /* not on subdivision, compute distance to next subdivision */
3720 ticks += ticks_one_subdivisions_worth - mod;
3723 if (ticks >= BBT_Time::ticks_per_beat) {
3724 ticks -= BBT_Time::ticks_per_beat;
3726 } else if (dir < 0) {
3728 /* round to previous (or same iff dir == RoundDownMaybe) */
3730 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3732 if (difference == 0 && dir == RoundDownAlways) {
3733 /* right on the subdivision, but force-rounding down,
3734 so the difference is just the subdivision ticks */
3735 difference = ticks_one_subdivisions_worth;
3738 if (ticks < difference) {
3739 ticks = BBT_Time::ticks_per_beat - ticks;
3741 ticks -= difference;
3745 /* round to nearest */
3748 /* compute the distance to the previous and next subdivision */
3750 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3752 /* closer to the next subdivision, so shift forward */
3754 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3756 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3758 if (ticks > BBT_Time::ticks_per_beat) {
3760 ticks -= BBT_Time::ticks_per_beat;
3761 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3764 } else if (rem > 0) {
3766 /* closer to previous subdivision, so shift backward */
3770 /* can't go backwards past zero, so ... */
3773 /* step back to previous beat */
3775 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3776 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3778 ticks = lrint (ticks - rem);
3779 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3782 /* on the subdivision, do nothing */
3786 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3792 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3794 Glib::Threads::RWLock::ReaderLock lm (lock);
3795 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3796 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3797 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3799 ticks -= beats * BBT_Time::ticks_per_beat;
3802 /* round to next (or same iff dir == RoundUpMaybe) */
3804 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3806 if (mod == 0 && dir == RoundUpMaybe) {
3807 /* right on the subdivision, which is fine, so do nothing */
3809 } else if (mod == 0) {
3810 /* right on the subdivision, so the difference is just the subdivision ticks */
3811 ticks += ticks_one_subdivisions_worth;
3814 /* not on subdivision, compute distance to next subdivision */
3816 ticks += ticks_one_subdivisions_worth - mod;
3819 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3820 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3821 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3822 // But I'm keeping it around, until we determine there are no terrible consequences.
3823 // if (ticks >= BBT_Time::ticks_per_beat) {
3824 // ticks -= BBT_Time::ticks_per_beat;
3827 } else if (dir < 0) {
3829 /* round to previous (or same iff dir == RoundDownMaybe) */
3831 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3833 if (difference == 0 && dir == RoundDownAlways) {
3834 /* right on the subdivision, but force-rounding down,
3835 so the difference is just the subdivision ticks */
3836 difference = ticks_one_subdivisions_worth;
3839 if (ticks < difference) {
3840 ticks = BBT_Time::ticks_per_beat - ticks;
3842 ticks -= difference;
3846 /* round to nearest */
3849 /* compute the distance to the previous and next subdivision */
3851 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3853 /* closer to the next subdivision, so shift forward */
3855 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3857 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3859 if (ticks > BBT_Time::ticks_per_beat) {
3861 ticks -= BBT_Time::ticks_per_beat;
3862 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3865 } else if (rem > 0) {
3867 /* closer to previous subdivision, so shift backward */
3871 /* can't go backwards past zero, so ... */
3874 /* step back to previous beat */
3876 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3877 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3879 ticks = lrint (ticks - rem);
3880 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3883 /* on the subdivision, do nothing */
3887 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3893 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3895 Glib::Threads::RWLock::ReaderLock lm (lock);
3897 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3898 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3903 /* find bar previous to 'frame' */
3908 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3910 } else if (dir > 0) {
3911 /* find bar following 'frame' */
3915 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3917 /* true rounding: find nearest bar */
3918 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3921 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3923 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3925 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3936 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3937 } else if (dir > 0) {
3938 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3940 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3949 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3950 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3952 Glib::Threads::RWLock::ReaderLock lm (lock);
3953 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3955 /* although the map handles negative beats, bbt doesn't. */
3960 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3964 while (pos >= 0 && pos < upper) {
3965 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3966 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3967 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3968 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3969 const double c = (tempo.type() == TempoSection::Constant) ? 0.0 : tempo.c_func();
3971 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, c));
3975 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3980 bbt.bars -= bbt.bars % bar_mod;
3984 while (pos >= 0 && pos < upper) {
3985 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3986 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3987 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3988 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3989 bbt.bars += bar_mod;
3995 TempoMap::tempo_section_at_frame (framepos_t frame) const
3997 Glib::Threads::RWLock::ReaderLock lm (lock);
3999 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4003 TempoMap::tempo_section_at_frame (framepos_t frame)
4005 Glib::Threads::RWLock::ReaderLock lm (lock);
4007 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4011 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4013 TempoSection* prev = 0;
4017 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4019 if ((*i)->is_tempo()) {
4020 t = static_cast<TempoSection*> (*i);
4024 if (prev && t->minute() > minute) {
4034 abort(); /*NOTREACHED*/
4040 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4042 TempoSection* prev = 0;
4046 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4048 if ((*i)->is_tempo()) {
4049 t = static_cast<TempoSection*> (*i);
4053 if (prev && t->minute() > minute) {
4063 abort(); /*NOTREACHED*/
4069 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4071 TempoSection* prev_t = 0;
4072 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4076 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4077 if ((*i)->is_tempo()) {
4078 t = static_cast<TempoSection*> (*i);
4079 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4089 /* don't use this to calculate length (the tempo is only correct for this frame).
4090 do that stuff based on the beat_at_frame and frame_at_beat api
4093 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4095 Glib::Threads::RWLock::ReaderLock lm (lock);
4097 const TempoSection* ts_at = 0;
4098 const TempoSection* ts_after = 0;
4099 Metrics::const_iterator i;
4102 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4104 if ((*i)->is_tempo()) {
4105 t = static_cast<TempoSection*> (*i);
4109 if (ts_at && (*i)->frame() > frame) {
4119 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4121 /* must be treated as constant tempo */
4122 return ts_at->frames_per_quarter_note (_frame_rate);
4126 TempoMap::meter_section_at_frame (framepos_t frame) const
4128 Glib::Threads::RWLock::ReaderLock lm (lock);
4129 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4133 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4135 Metrics::const_iterator i;
4136 MeterSection* prev = 0;
4140 for (i = metrics.begin(); i != metrics.end(); ++i) {
4142 if (!(*i)->is_tempo()) {
4143 m = static_cast<MeterSection*> (*i);
4145 if (prev && (*i)->minute() > minute) {
4155 abort(); /*NOTREACHED*/
4162 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4164 MeterSection* prev_m = 0;
4166 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4168 if (!(*i)->is_tempo()) {
4169 m = static_cast<MeterSection*> (*i);
4170 if (prev_m && m->beat() > beat) {
4181 TempoMap::meter_section_at_beat (double beat) const
4183 Glib::Threads::RWLock::ReaderLock lm (lock);
4184 return meter_section_at_beat_locked (_metrics, beat);
4188 TempoMap::meter_at_frame (framepos_t frame) const
4190 TempoMetric m (metric_at (frame));
4195 TempoMap::fix_legacy_session ()
4197 MeterSection* prev_m = 0;
4198 TempoSection* prev_t = 0;
4200 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4204 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4206 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4209 m->set_minute (0.0);
4210 m->set_position_lock_style (AudioTime);
4215 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4216 + (m->bbt().beats - 1)
4217 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4219 m->set_beat (start);
4220 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4221 + (m->bbt().beats - 1)
4222 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4223 m->set_pulse (start_beat / prev_m->note_divisor());
4226 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4234 t->set_minute (0.0);
4235 t->set_position_lock_style (AudioTime);
4241 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4242 + (t->legacy_bbt().beats - 1)
4243 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4245 t->set_pulse (beat / prev_m->note_divisor());
4247 /* really shouldn't happen but.. */
4248 t->set_pulse (beat / 4.0);
4257 TempoMap::get_state ()
4259 Metrics::const_iterator i;
4260 XMLNode *root = new XMLNode ("TempoMap");
4263 Glib::Threads::RWLock::ReaderLock lm (lock);
4264 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4265 root->add_child_nocopy ((*i)->get_state());
4273 TempoMap::set_state (const XMLNode& node, int /*version*/)
4276 Glib::Threads::RWLock::WriterLock lm (lock);
4279 XMLNodeConstIterator niter;
4280 Metrics old_metrics (_metrics);
4283 nlist = node.children();
4285 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4286 XMLNode* child = *niter;
4288 if (child->name() == TempoSection::xml_state_node_name) {
4291 TempoSection* ts = new TempoSection (*child, _frame_rate);
4292 _metrics.push_back (ts);
4295 catch (failed_constructor& err){
4296 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4297 _metrics = old_metrics;
4298 old_metrics.clear();
4302 } else if (child->name() == MeterSection::xml_state_node_name) {
4305 MeterSection* ms = new MeterSection (*child, _frame_rate);
4306 _metrics.push_back (ms);
4309 catch (failed_constructor& err) {
4310 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4311 _metrics = old_metrics;
4312 old_metrics.clear();
4318 if (niter == nlist.end()) {
4319 MetricSectionSorter cmp;
4320 _metrics.sort (cmp);
4323 /* check for legacy sessions where bbt was the base musical unit for tempo */
4324 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4326 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4327 if (t->legacy_bbt().bars != 0) {
4328 fix_legacy_session();
4335 /* check for multiple tempo/meters at the same location, which
4336 ardour2 somehow allowed.
4339 Metrics::iterator prev = _metrics.end();
4340 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4341 if (prev != _metrics.end()) {
4343 MeterSection* prev_m;
4345 TempoSection* prev_t;
4346 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4347 if (prev_m->pulse() == ms->pulse()) {
4348 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4349 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4352 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4353 if (prev_t->pulse() == ts->pulse()) {
4354 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4355 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4363 recompute_map (_metrics);
4365 Metrics::const_iterator d = old_metrics.begin();
4366 while (d != old_metrics.end()) {
4370 old_metrics.clear ();
4373 PropertyChanged (PropertyChange ());
4379 TempoMap::dump (std::ostream& o) const
4381 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4382 const MeterSection* m;
4383 const TempoSection* t;
4384 const TempoSection* prev_t = 0;
4386 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4388 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4389 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4390 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4391 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4392 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4394 o << " current : " << t->note_types_per_minute()
4395 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4396 o << " previous : " << prev_t->note_types_per_minute()
4397 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4398 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4399 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4400 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4401 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4404 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4405 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4406 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4407 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4410 o << "------" << std::endl;
4414 TempoMap::n_tempos() const
4416 Glib::Threads::RWLock::ReaderLock lm (lock);
4419 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4420 if ((*i)->is_tempo()) {
4429 TempoMap::n_meters() const
4431 Glib::Threads::RWLock::ReaderLock lm (lock);
4434 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4435 if (!(*i)->is_tempo()) {
4444 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4446 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4447 if ((*i)->frame() >= where && !(*i)->initial ()) {
4451 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4452 gui_set_meter_position (ms, (*i)->frame() + amount);
4455 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4456 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4461 PropertyChanged (PropertyChange ());
4465 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4469 std::list<MetricSection*> metric_kill_list;
4471 TempoSection* last_tempo = NULL;
4472 MeterSection* last_meter = NULL;
4473 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4474 bool meter_after = false; // is there a meter marker likewise?
4476 Glib::Threads::RWLock::WriterLock lm (lock);
4477 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4478 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4479 metric_kill_list.push_back(*i);
4480 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4483 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4487 else if ((*i)->frame() >= where) {
4488 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4489 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4490 if ((*i)->frame() == where) {
4491 // marker was immediately after end of range
4492 tempo_after = dynamic_cast<TempoSection*> (*i);
4493 meter_after = dynamic_cast<MeterSection*> (*i);
4499 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4500 if (last_tempo && !tempo_after) {
4501 metric_kill_list.remove(last_tempo);
4502 last_tempo->set_minute (minute_at_frame (where));
4505 if (last_meter && !meter_after) {
4506 metric_kill_list.remove(last_meter);
4507 last_meter->set_minute (minute_at_frame (where));
4511 //remove all the remaining metrics
4512 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4513 _metrics.remove(*i);
4518 recompute_map (_metrics);
4521 PropertyChanged (PropertyChange ());
4525 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4526 * pos can be -ve, if required.
4529 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4531 Glib::Threads::RWLock::ReaderLock lm (lock);
4532 const double frame_qn = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
4534 return frame_at_minute (minute_at_quarter_note_locked (_metrics, frame_qn + beats.to_double()));
4538 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4540 Glib::Threads::RWLock::ReaderLock lm (lock);
4542 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4543 pos_bbt.ticks += op.ticks;
4544 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4546 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4548 pos_bbt.beats += op.beats;
4549 /* the meter in effect will start on the bar */
4550 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();
4551 while (pos_bbt.beats >= divisions_per_bar + 1) {
4553 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4554 pos_bbt.beats -= divisions_per_bar;
4556 pos_bbt.bars += op.bars;
4558 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4561 /** Count the number of beats that are equivalent to distance when going forward,
4565 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4567 Glib::Threads::RWLock::ReaderLock lm (lock);
4569 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4573 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4579 operator<< (std::ostream& o, const Meter& m) {
4580 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4584 operator<< (std::ostream& o, const Tempo& t) {
4585 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4589 operator<< (std::ostream& o, const MetricSection& section) {
4591 o << "MetricSection @ " << section.frame() << ' ';
4593 const TempoSection* ts;
4594 const MeterSection* ms;
4596 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4597 o << *((const Tempo*) ts);
4598 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4599 o << *((const Meter*) ms);