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, 120.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)
95 XMLProperty const * prop;
101 _legacy_bbt = BBT_Time (0, 0, 0);
103 if ((prop = node.property ("start")) != 0) {
104 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
108 /* legacy session - start used to be in bbt*/
111 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
115 if ((prop = node.property ("pulse")) != 0) {
116 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
117 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
123 if ((prop = node.property ("frame")) != 0) {
124 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
125 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
126 throw failed_constructor();
128 set_minute (minute_at_frame (frame));
132 /* XX replace old beats-per-minute name with note-types-per-minute */
133 if ((prop = node.property ("beats-per-minute")) != 0) {
134 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
135 error << _("TempoSection XML node has an illegal \"beats-per-minute\" value") << endmsg;
136 throw failed_constructor();
140 if ((prop = node.property ("note-type")) == 0) {
141 /* older session, make note type be quarter by default */
144 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
145 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
146 throw failed_constructor();
150 /* XX replace old end-beats-per-minute name with note-types-per-minute */
151 if ((prop = node.property ("end-beats-per-minute")) != 0) {
152 if (sscanf (prop->value().c_str(), "%lf", &_end_note_types_per_minute) != 1 || _end_note_types_per_minute < 0.0) {
153 info << _("TempoSection XML node has an illegal \"in-beats-per-minute\" value") << endmsg;
154 //throw failed_constructor();
155 _end_note_types_per_minute = _note_types_per_minute;
160 if ((prop = node.property ("movable")) == 0) {
161 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
162 throw failed_constructor();
165 set_initial (!string_is_affirmative (prop->value()));
167 if ((prop = node.property ("active")) == 0) {
168 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
171 set_active (string_is_affirmative (prop->value()));
174 if ((prop = node.property ("lock-style")) == 0) {
176 set_position_lock_style (MusicTime);
178 set_position_lock_style (AudioTime);
181 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
184 if ((prop = node.property ("locked-to-meter")) == 0) {
186 set_locked_to_meter (true);
188 set_locked_to_meter (false);
191 set_locked_to_meter (string_is_affirmative (prop->value()));
194 /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
196 set_locked_to_meter (true);
201 TempoSection::get_state() const
203 XMLNode *root = new XMLNode (xml_state_node_name);
207 snprintf (buf, sizeof (buf), "%lf", pulse());
208 root->add_property ("pulse", buf);
209 snprintf (buf, sizeof (buf), "%li", frame());
210 root->add_property ("frame", buf);
211 snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
212 root->add_property ("beats-per-minute", buf);
213 snprintf (buf, sizeof (buf), "%lf", _note_type);
214 root->add_property ("note-type", buf);
215 snprintf (buf, sizeof (buf), "%lf", _end_note_types_per_minute);
216 root->add_property ("end-beats-per-minute", buf);
217 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
218 root->add_property ("movable", buf);
219 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
220 root->add_property ("active", buf);
221 root->add_property ("lock-style", enum_2_string (position_lock_style()));
222 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
227 /** returns the Tempo at the session-relative minute.
230 TempoSection::tempo_at_minute (const double& m) const
232 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
234 return Tempo (note_types_per_minute(), note_type());
237 return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
240 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
241 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
242 * @param p the pulse used to calculate the returned minute for constant tempi
243 * @return the minute at the supplied tempo
245 * note that the note_type is currently ignored in this function. see below.
249 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
250 * there should be no ramp between the two even if we are ramped.
251 * in other words a ramp should only place a curve on note_types_per_minute.
252 * we should be able to use Tempo note type here, but the above
253 * complicates things a bit.
256 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
258 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
260 return ((p - pulse()) / pulses_per_minute()) + minute();
263 return _time_at_tempo (ntpm) + minute();
266 /** returns the Tempo at the supplied whole-note pulse.
269 TempoSection::tempo_at_pulse (const double& p) const
271 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
274 return Tempo (note_types_per_minute(), note_type());
277 return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
280 /** returns the whole-note pulse where a tempo in note types per minute occurs.
281 * constant tempi require minute m.
282 * @param ntpm the note types per minute value used to calculate the returned pulse
283 * @param m the minute used to calculate the returned pulse if the tempo is constant
284 * @return the whole-note pulse at the supplied tempo
286 * note that note_type is currently ignored in this function. see minute_at_tempo().
288 * for constant tempi, this is anaologous to pulse_at_minute().
291 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
293 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
295 return ((m - minute()) * pulses_per_minute()) + pulse();
298 return _pulse_at_tempo (ntpm) + pulse();
301 /** returns the whole-note pulse at the supplied session-relative minute.
304 TempoSection::pulse_at_minute (const double& m) const
306 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
308 return ((m - minute()) * pulses_per_minute()) + pulse();
311 return _pulse_at_time (m - minute()) + pulse();
314 /** returns the session-relative minute at the supplied whole-note pulse.
317 TempoSection::minute_at_pulse (const double& p) const
319 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
321 return ((p - pulse()) / pulses_per_minute()) + minute();
324 return _time_at_pulse (p - pulse()) + minute();
327 /** returns thw whole-note pulse at session frame position f.
328 * @param f the frame position.
329 * @return the position in whole-note pulses corresponding to f
331 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
334 TempoSection::pulse_at_frame (const framepos_t& f) const
336 const bool constant = type() == Constant || _c == 0.0 || (initial() && f < frame());
338 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
341 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
345 TempoSection::frame_at_pulse (const double& p) const
347 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
349 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
352 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
360 Tt----|-----------------*|
361 Ta----|--------------|* |
367 _______________|___|____
368 time a t (next tempo)
371 Duration in beats at time a is the integral of some Tempo function.
372 In our case, the Tempo function (Tempo at time t) is
375 with function constant
380 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
381 b(t) = T0(e^(ct) - 1) / c
383 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:
384 t(b) = log((c.b / T0) + 1) / c
386 The time t at which Tempo T occurs is a as above:
387 t(T) = log(T / T0) / c
389 The beat at which a Tempo T occurs is:
392 The Tempo at which beat b occurs is:
395 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
396 Our problem is that we usually don't know t.
397 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.
398 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
399 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
401 By substituting our expanded t as a in the c function above, our problem is reduced to:
402 c = T0 (e^(log (Ta / T0)) - 1) / b
404 Of course the word 'beat' has been left loosely defined above.
405 In music, a beat is defined by the musical pulse (which comes from the tempo)
406 and the meter in use at a particular time (how many pulse divisions there are in one bar).
407 It would be more accurate to substitute the work 'pulse' for 'beat' above.
411 We can now store c for future time calculations.
412 If the following tempo section (the one that defines c in conjunction with this one)
413 is changed or moved, c is no longer valid.
415 The public methods are session-relative.
417 Most of this stuff is taken from this paper:
420 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
423 Zurich University of Arts
424 Institute for Computer Music and Sound Technology
426 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
430 /** compute this ramp's function constant from some tempo-pulse point
431 * @param end_npm end tempo (in note types per minute)
432 * @param end_pulse duration (pulses into global start) of some other position.
433 * @return the calculated function constant
436 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
438 if (note_types_per_minute() == end_npm || type() == Constant) {
442 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
443 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
446 /** compute the function constant from some tempo-time point.
447 * @param end_npm tempo (note types/min.)
448 * @param end_minute distance (in minutes) from session origin
449 * @return the calculated function constant
452 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
454 if (note_types_per_minute() == end_npm || type() == Constant) {
458 return c_func (end_npm, end_minute - minute());
461 /* position function */
463 TempoSection::a_func (double end_npm, double c) const
465 return log (end_npm / note_types_per_minute()) / c;
468 /*function constant*/
470 TempoSection::c_func (double end_npm, double end_time) const
472 return log (end_npm / note_types_per_minute()) / end_time;
475 /* tempo in note types per minute at time in minutes */
477 TempoSection::_tempo_at_time (const double& time) const
479 return exp (_c * time) * note_types_per_minute();
482 /* time in minutes at tempo in note types per minute */
484 TempoSection::_time_at_tempo (const double& npm) const
486 return log (npm / note_types_per_minute()) / _c;
489 /* pulse at tempo in note types per minute */
491 TempoSection::_pulse_at_tempo (const double& npm) const
493 return ((npm - note_types_per_minute()) / _c) / _note_type;
496 /* tempo in note types per minute at pulse */
498 TempoSection::_tempo_at_pulse (const double& pulse) const
500 return (pulse * _note_type * _c) + note_types_per_minute();
503 /* pulse at time in minutes */
505 TempoSection::_pulse_at_time (const double& time) const
507 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
510 /* time in minutes at pulse */
512 TempoSection::_time_at_pulse (const double& pulse) const
514 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
517 /***********************************************************************/
519 const string MeterSection::xml_state_node_name = "Meter";
521 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
522 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
524 XMLProperty const * prop;
529 framepos_t frame = 0;
530 pair<double, BBT_Time> start;
532 if ((prop = node.property ("start")) != 0) {
533 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
537 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
539 /* legacy session - start used to be in bbt*/
540 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
545 if ((prop = node.property ("pulse")) != 0) {
546 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
547 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
552 if ((prop = node.property ("beat")) != 0) {
553 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
554 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
560 if ((prop = node.property ("bbt")) == 0) {
561 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
562 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
566 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
567 throw failed_constructor();
573 if ((prop = node.property ("frame")) != 0) {
574 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
575 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
576 throw failed_constructor();
578 set_minute (minute_at_frame (frame));
582 /* beats-per-bar is old; divisions-per-bar is new */
584 if ((prop = node.property ("divisions-per-bar")) == 0) {
585 if ((prop = node.property ("beats-per-bar")) == 0) {
586 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
587 throw failed_constructor();
590 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
591 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
592 throw failed_constructor();
595 if ((prop = node.property ("note-type")) == 0) {
596 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
597 throw failed_constructor();
599 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
600 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
601 throw failed_constructor();
604 if ((prop = node.property ("movable")) == 0) {
605 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
606 throw failed_constructor();
609 set_initial (!string_is_affirmative (prop->value()));
611 if ((prop = node.property ("lock-style")) == 0) {
612 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
614 set_position_lock_style (MusicTime);
616 set_position_lock_style (AudioTime);
619 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
624 MeterSection::get_state() const
626 XMLNode *root = new XMLNode (xml_state_node_name);
630 snprintf (buf, sizeof (buf), "%lf", pulse());
631 root->add_property ("pulse", buf);
632 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
636 root->add_property ("bbt", buf);
637 snprintf (buf, sizeof (buf), "%lf", beat());
638 root->add_property ("beat", buf);
639 snprintf (buf, sizeof (buf), "%lf", _note_type);
640 root->add_property ("note-type", buf);
641 snprintf (buf, sizeof (buf), "%li", frame());
642 root->add_property ("frame", buf);
643 root->add_property ("lock-style", enum_2_string (position_lock_style()));
644 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
645 root->add_property ("divisions-per-bar", buf);
646 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
647 root->add_property ("movable", buf);
652 /***********************************************************************/
656 Tempo determines the rate of musical pulse determined by its components
657 note types per minute - the rate per minute of the whole note divisor _note_type
658 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
659 Meter divides the musical pulse into measures and beats according to its components
663 TempoSection - translates between time, musical pulse and tempo.
664 has a musical location in whole notes (pulses).
665 has a time location in minutes.
666 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
667 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
669 MeterSection - translates between BBT, meter-based beat and musical pulse.
670 has a musical location in whole notes (pulses)
671 has a musical location in meter-based beats
672 has a musical location in BBT time
673 has a time location expressed in minutes.
675 TempoSection and MeterSection may be locked to either audio or music (position lock style).
676 The lock style determines the location type to be kept as a reference when location is recalculated.
678 The first tempo and meter are special. they must move together, and are locked to audio.
679 Audio locked tempi which lie before the first meter are made inactive.
681 Recomputing the map is the process where the 'missing' location types are calculated.
682 We construct the tempo map by first using the locked location type of each section
683 to determine non-locked location types (pulse or minute position).
684 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
686 Having done this, we can now traverse the Metrics list by pulse or minute
687 to query its relevant meter/tempo.
689 It is important to keep the _metrics in an order that makes sense.
690 Because ramped MusicTime and AudioTime tempos can interact with each other,
691 reordering is frequent. Care must be taken to keep _metrics in a solved state.
692 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
696 Music and audio-locked objects may seem interchangeable on the surface, but when translating
697 between audio samples and beat, remember that a sample is only a quantised approximation
698 of the actual time (in minutes) of a beat.
699 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
700 mean that this frame is the actual location in time of 1|3|0.
702 You cannot use a frame measurement to determine beat distance except under special circumstances
703 (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).
705 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
706 sample space the user is operating at to be translated correctly to the object.
708 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
709 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
710 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
712 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
714 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
715 The result is rounded to audio frames.
716 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
719 frame_at_beat (beat_at_frame (frame)) == frame
721 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
723 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
724 frames_between_quarter_notes () eliminats this effect when determining time duration
725 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
727 The above pointless example could instead do:
728 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
730 The Shaggs - Things I Wonder
731 https://www.youtube.com/watch?v=9wQK6zMJOoQ
734 struct MetricSectionSorter {
735 bool operator() (const MetricSection* a, const MetricSection* b) {
736 return a->pulse() < b->pulse();
740 struct MetricSectionFrameSorter {
741 bool operator() (const MetricSection* a, const MetricSection* b) {
742 return a->frame() < b->frame();
746 TempoMap::TempoMap (framecnt_t fr)
749 BBT_Time start (1, 1, 0);
751 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
752 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
754 t->set_initial (true);
755 t->set_locked_to_meter (true);
757 m->set_initial (true);
759 /* note: frame time is correct (zero) for both of these */
761 _metrics.push_back (t);
762 _metrics.push_back (m);
766 TempoMap::TempoMap (TempoMap const & other)
768 _frame_rate = other._frame_rate;
769 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
770 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
771 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
774 TempoSection* new_section = new TempoSection (*ts);
775 _metrics.push_back (new_section);
777 MeterSection* new_section = new MeterSection (*ms);
778 _metrics.push_back (new_section);
784 TempoMap::operator= (TempoMap const & other)
786 if (&other != this) {
787 _frame_rate = other._frame_rate;
789 Metrics::const_iterator d = _metrics.begin();
790 while (d != _metrics.end()) {
796 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
797 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
798 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
801 TempoSection* new_section = new TempoSection (*ts);
802 _metrics.push_back (new_section);
804 MeterSection* new_section = new MeterSection (*ms);
805 _metrics.push_back (new_section);
810 PropertyChanged (PropertyChange());
815 TempoMap::~TempoMap ()
817 Metrics::const_iterator d = _metrics.begin();
818 while (d != _metrics.end()) {
826 TempoMap::frame_at_minute (const double time) const
828 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
832 TempoMap::minute_at_frame (const framepos_t frame) const
834 return (frame / (double) _frame_rate) / 60.0;
838 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
840 bool removed = false;
843 Glib::Threads::RWLock::WriterLock lm (lock);
844 if ((removed = remove_tempo_locked (tempo))) {
845 if (complete_operation) {
846 recompute_map (_metrics);
851 if (removed && complete_operation) {
852 PropertyChanged (PropertyChange ());
857 TempoMap::remove_tempo_locked (const TempoSection& tempo)
861 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
862 if (dynamic_cast<TempoSection*> (*i) != 0) {
863 if (tempo.frame() == (*i)->frame()) {
864 if (!(*i)->initial()) {
877 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
879 bool removed = false;
882 Glib::Threads::RWLock::WriterLock lm (lock);
883 if ((removed = remove_meter_locked (tempo))) {
884 if (complete_operation) {
885 recompute_map (_metrics);
890 if (removed && complete_operation) {
891 PropertyChanged (PropertyChange ());
896 TempoMap::remove_meter_locked (const MeterSection& meter)
899 if (meter.position_lock_style() == AudioTime) {
900 /* remove meter-locked tempo */
901 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
903 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
904 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
913 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
914 if (dynamic_cast<MeterSection*> (*i) != 0) {
915 if (meter.frame() == (*i)->frame()) {
916 if (!(*i)->initial()) {
929 TempoMap::do_insert (MetricSection* section)
931 bool need_add = true;
932 /* we only allow new meters to be inserted on beat 1 of an existing
936 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
938 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
940 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
941 corrected.second.beats = 1;
942 corrected.second.ticks = 0;
943 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
944 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
945 m->bbt(), corrected.second) << endmsg;
946 //m->set_pulse (corrected);
950 /* Look for any existing MetricSection that is of the same type and
951 in the same bar as the new one, and remove it before adding
952 the new one. Note that this means that if we find a matching,
953 existing section, we can break out of the loop since we're
954 guaranteed that there is only one such match.
957 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
959 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
960 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
961 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
962 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
964 if (tempo && insert_tempo) {
967 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
968 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
970 if (tempo->initial()) {
972 /* can't (re)move this section, so overwrite
973 * its data content (but not its properties as
977 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
978 (*i)->set_position_lock_style (AudioTime);
987 } else if (meter && insert_meter) {
991 bool const ipm = insert_meter->position_lock_style() == MusicTime;
993 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
995 if (meter->initial()) {
997 /* can't (re)move this section, so overwrite
998 * its data content (but not its properties as
1002 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
1003 (*i)->set_position_lock_style (AudioTime);
1013 /* non-matching types, so we don't care */
1017 /* Add the given MetricSection, if we didn't just reset an existing
1022 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
1023 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
1024 Metrics::iterator i;
1027 TempoSection* prev_t = 0;
1029 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1030 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1031 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1034 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1038 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1042 prev_t = dynamic_cast<TempoSection*> (*i);
1045 } else if (insert_tempo) {
1046 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1047 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1050 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1051 const bool lm = insert_tempo->locked_to_meter();
1052 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1053 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1060 _metrics.insert (i, section);
1064 /* user supplies the exact pulse if pls == MusicTime */
1066 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1068 if (tempo.note_types_per_minute() <= 0.0) {
1069 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1073 TempoSection* ts = 0;
1074 TempoSection* prev_tempo = 0;
1076 Glib::Threads::RWLock::WriterLock lm (lock);
1077 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true);
1078 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1080 if ((*i)->is_tempo()) {
1081 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1083 bool const ipm = ts->position_lock_style() == MusicTime;
1084 bool const lm = ts->locked_to_meter();
1085 if ((ipm && this_t->pulse() == ts->pulse()) || (!ipm && this_t->frame() == ts->frame())
1086 || (lm && this_t->pulse() == ts->pulse())) {
1087 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1088 prev_tempo->set_end_note_types_per_minute (ts->note_types_per_minute());
1092 prev_tempo = this_t;
1095 recompute_map (_metrics);
1098 PropertyChanged (PropertyChange ());
1104 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1106 if (tempo.note_types_per_minute() <= 0.0) {
1107 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1111 const bool locked_to_meter = ts.locked_to_meter();
1112 TempoSection* new_ts = 0;
1115 Glib::Threads::RWLock::WriterLock lm (lock);
1116 TempoSection& first (first_tempo());
1117 if (!ts.initial()) {
1118 if (locked_to_meter) {
1120 /* cannot move a meter-locked tempo section */
1121 *static_cast<Tempo*>(&ts) = tempo;
1122 recompute_map (_metrics);
1125 remove_tempo_locked (ts);
1126 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
1128 if (new_ts && new_ts->type() == TempoSection::Constant) {
1129 new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
1131 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1133 if ((*i)->is_tempo()) {
1134 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1136 bool const ipm = new_ts->position_lock_style() == MusicTime;
1137 bool const lm = new_ts->locked_to_meter();
1138 if ((ipm && this_t->pulse() > new_ts->pulse()) || (!ipm && this_t->frame() > new_ts->frame())
1139 || (lm && this_t->pulse() > new_ts->pulse())) {
1140 new_ts->set_end_note_types_per_minute (tempo.end_note_types_per_minute());
1150 first.set_pulse (0.0);
1151 first.set_minute (minute_at_frame (frame));
1152 first.set_position_lock_style (AudioTime);
1153 first.set_locked_to_meter (true);
1155 /* cannot move the first tempo section */
1156 *static_cast<Tempo*>(&first) = tempo;
1159 recompute_map (_metrics);
1162 PropertyChanged (PropertyChange ());
1166 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1167 , PositionLockStyle pls, bool recompute, bool locked_to_meter)
1169 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1170 t->set_locked_to_meter (locked_to_meter);
1175 if (pls == AudioTime) {
1176 solve_map_minute (_metrics, t, t->minute());
1178 solve_map_pulse (_metrics, t, t->pulse());
1180 recompute_meters (_metrics);
1187 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1189 MeterSection* m = 0;
1191 Glib::Threads::RWLock::WriterLock lm (lock);
1192 m = add_meter_locked (meter, beat, where, frame, pls, true);
1197 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1202 PropertyChanged (PropertyChange ());
1207 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1210 Glib::Threads::RWLock::WriterLock lm (lock);
1211 const double beat = beat_at_bbt_locked (_metrics, where);
1213 if (!ms.initial()) {
1214 remove_meter_locked (ms);
1215 add_meter_locked (meter, beat, where, frame, pls, true);
1217 MeterSection& first (first_meter());
1218 TempoSection& first_t (first_tempo());
1219 /* cannot move the first meter section */
1220 *static_cast<Meter*>(&first) = meter;
1221 first.set_position_lock_style (AudioTime);
1222 first.set_pulse (0.0);
1223 first.set_minute (minute_at_frame (frame));
1224 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1225 first.set_beat (beat);
1226 first_t.set_minute (first.minute());
1227 first_t.set_locked_to_meter (true);
1228 first_t.set_pulse (0.0);
1229 first_t.set_position_lock_style (AudioTime);
1230 recompute_map (_metrics);
1234 PropertyChanged (PropertyChange ());
1238 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1240 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1241 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1242 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1243 TempoSection* mlt = 0;
1245 if (pls == AudioTime) {
1246 /* add meter-locked tempo */
1247 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), AudioTime, true, true);
1255 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1257 bool solved = false;
1259 do_insert (new_meter);
1263 if (pls == AudioTime) {
1264 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1265 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1266 fudge frame so that the meter ends up at its BBT position instead.
1269 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1272 solved = solve_map_bbt (_metrics, new_meter, where);
1273 /* required due to resetting the pulse of meter-locked tempi above.
1274 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1275 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1277 recompute_map (_metrics);
1281 if (!solved && recompute) {
1282 /* if this has failed to solve, there is little we can do other than to ensure that
1283 the new map is recalculated.
1285 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1286 recompute_map (_metrics);
1293 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1295 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1298 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1299 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1304 Glib::Threads::RWLock::WriterLock lm (lock);
1305 *((Tempo*) t) = newtempo;
1306 recompute_map (_metrics);
1308 PropertyChanged (PropertyChange ());
1315 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1317 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1320 TempoSection* first;
1321 Metrics::iterator i;
1323 /* find the TempoSection immediately preceding "where"
1326 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1328 if ((*i)->frame() > where) {
1334 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1347 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1357 Glib::Threads::RWLock::WriterLock lm (lock);
1358 /* cannot move the first tempo section */
1359 *((Tempo*)prev) = newtempo;
1360 recompute_map (_metrics);
1363 PropertyChanged (PropertyChange ());
1367 TempoMap::first_meter () const
1369 const MeterSection *m = 0;
1371 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1372 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1377 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1378 abort(); /*NOTREACHED*/
1383 TempoMap::first_meter ()
1385 MeterSection *m = 0;
1387 /* CALLER MUST HOLD LOCK */
1389 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1390 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1395 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1396 abort(); /*NOTREACHED*/
1401 TempoMap::first_tempo () const
1403 const TempoSection *t = 0;
1405 /* CALLER MUST HOLD LOCK */
1407 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1408 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1418 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1419 abort(); /*NOTREACHED*/
1424 TempoMap::first_tempo ()
1426 TempoSection *t = 0;
1428 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1429 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1439 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1440 abort(); /*NOTREACHED*/
1444 TempoMap::recompute_tempi (Metrics& metrics)
1446 TempoSection* prev_t = 0;
1448 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1451 if ((*i)->is_tempo()) {
1452 t = static_cast<TempoSection*> (*i);
1464 if (t->position_lock_style() == AudioTime) {
1465 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1466 if (!t->locked_to_meter()) {
1467 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1471 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1472 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1480 prev_t->set_c (0.0);
1483 /* tempos must be positioned correctly.
1484 the current approach is to use a meter's bbt time as its base position unit.
1485 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1486 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1489 TempoMap::recompute_meters (Metrics& metrics)
1491 MeterSection* meter = 0;
1492 MeterSection* prev_m = 0;
1494 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1495 if (!(*mi)->is_tempo()) {
1496 meter = static_cast<MeterSection*> (*mi);
1497 if (meter->position_lock_style() == AudioTime) {
1499 pair<double, BBT_Time> b_bbt;
1500 TempoSection* meter_locked_tempo = 0;
1501 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1503 if ((*ii)->is_tempo()) {
1504 t = static_cast<TempoSection*> (*ii);
1505 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1506 meter_locked_tempo = t;
1513 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1514 if (beats + prev_m->beat() != meter->beat()) {
1515 /* reordering caused a bbt change */
1517 beats = meter->beat() - prev_m->beat();
1518 b_bbt = make_pair (beats + prev_m->beat()
1519 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1520 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1522 } else if (!meter->initial()) {
1523 b_bbt = make_pair (meter->beat(), meter->bbt());
1524 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1527 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1529 if (meter_locked_tempo) {
1530 meter_locked_tempo->set_pulse (pulse);
1532 meter->set_beat (b_bbt);
1533 meter->set_pulse (pulse);
1538 pair<double, BBT_Time> b_bbt;
1540 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1541 if (beats + prev_m->beat() != meter->beat()) {
1542 /* reordering caused a bbt change */
1543 b_bbt = make_pair (beats + prev_m->beat()
1544 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1546 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1548 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1550 /* shouldn't happen - the first is audio-locked */
1551 pulse = pulse_at_beat_locked (metrics, meter->beat());
1552 b_bbt = make_pair (meter->beat(), meter->bbt());
1555 meter->set_beat (b_bbt);
1556 meter->set_pulse (pulse);
1557 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1566 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1568 /* CALLER MUST HOLD WRITE LOCK */
1570 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1573 /* silly call from Session::process() during startup
1578 recompute_tempi (metrics);
1579 recompute_meters (metrics);
1583 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1585 Glib::Threads::RWLock::ReaderLock lm (lock);
1586 TempoMetric m (first_meter(), first_tempo());
1588 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1589 at something, because we insert the default tempo and meter during
1590 TempoMap construction.
1592 now see if we can find better candidates.
1595 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1597 if ((*i)->frame() > frame) {
1611 /* XX meters only */
1613 TempoMap::metric_at (BBT_Time bbt) const
1615 Glib::Threads::RWLock::ReaderLock lm (lock);
1616 TempoMetric m (first_meter(), first_tempo());
1618 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1619 at something, because we insert the default tempo and meter during
1620 TempoMap construction.
1622 now see if we can find better candidates.
1625 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1627 if (!(*i)->is_tempo()) {
1628 mw = static_cast<MeterSection*> (*i);
1629 BBT_Time section_start (mw->bbt());
1631 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1642 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1643 * @param frame The session frame position.
1644 * @return The beat duration according to the tempo map at the supplied frame.
1646 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1647 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1649 * This function uses both tempo and meter.
1652 TempoMap::beat_at_frame (const framecnt_t& frame) const
1654 Glib::Threads::RWLock::ReaderLock lm (lock);
1656 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1659 /* This function uses both tempo and meter.*/
1661 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1663 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1664 MeterSection* prev_m = 0;
1665 MeterSection* next_m = 0;
1667 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1668 if (!(*i)->is_tempo()) {
1669 if (prev_m && (*i)->minute() > minute) {
1670 next_m = static_cast<MeterSection*> (*i);
1673 prev_m = static_cast<MeterSection*> (*i);
1677 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1679 /* audio locked meters fake their beat */
1680 if (next_m && next_m->beat() < beat) {
1681 return next_m->beat();
1687 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1688 * @param beat The BBT (meter-based) beat.
1689 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1691 * This function uses both tempo and meter.
1694 TempoMap::frame_at_beat (const double& beat) const
1696 Glib::Threads::RWLock::ReaderLock lm (lock);
1698 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1701 /* meter & tempo section based */
1703 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1705 MeterSection* prev_m = 0;
1706 TempoSection* prev_t = 0;
1710 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1711 if (!(*i)->is_tempo()) {
1712 m = static_cast<MeterSection*> (*i);
1713 if (prev_m && m->beat() > beat) {
1723 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1724 if ((*i)->is_tempo()) {
1725 t = static_cast<TempoSection*> (*i);
1731 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1740 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1743 /** Returns a Tempo corresponding to the supplied frame position.
1744 * @param frame The audio frame.
1745 * @return a Tempo according to the tempo map at the supplied frame.
1749 TempoMap::tempo_at_frame (const framepos_t& frame) const
1751 Glib::Threads::RWLock::ReaderLock lm (lock);
1753 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1757 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1759 TempoSection* prev_t = 0;
1763 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1764 if ((*i)->is_tempo()) {
1765 t = static_cast<TempoSection*> (*i);
1769 if ((prev_t) && t->minute() > minute) {
1770 /* t is the section past frame */
1771 return prev_t->tempo_at_minute (minute);
1777 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1780 /** returns the frame at which the supplied tempo occurs, or
1781 * the frame of the last tempo section (search exhausted)
1782 * only the position of the first occurence will be returned
1786 TempoMap::frame_at_tempo (const Tempo& tempo) const
1788 Glib::Threads::RWLock::ReaderLock lm (lock);
1790 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1794 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1796 TempoSection* prev_t = 0;
1797 const double tempo_bpm = tempo.note_types_per_minute();
1799 Metrics::const_iterator i;
1801 for (i = metrics.begin(); i != metrics.end(); ++i) {
1803 if ((*i)->is_tempo()) {
1804 t = static_cast<TempoSection*> (*i);
1812 if (t->note_types_per_minute() == tempo_bpm) {
1817 const double prev_t_bpm = prev_t->note_types_per_minute();
1818 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1819 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1820 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1821 || (prev_t_end_bpm == tempo_bpm)) {
1823 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1830 return prev_t->minute();
1834 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1836 TempoSection* prev_t = 0;
1840 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1841 if ((*i)->is_tempo()) {
1842 t = static_cast<TempoSection*> (*i);
1846 if ((prev_t) && t->pulse() > pulse) {
1847 /* t is the section past frame */
1848 return prev_t->tempo_at_pulse (pulse);
1854 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1858 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1860 TempoSection* prev_t = 0;
1861 const double tempo_bpm = tempo.note_types_per_minute();
1863 Metrics::const_iterator i;
1865 for (i = metrics.begin(); i != metrics.end(); ++i) {
1867 if ((*i)->is_tempo()) {
1868 t = static_cast<TempoSection*> (*i);
1874 const double t_bpm = t->note_types_per_minute();
1876 if (t_bpm == tempo_bpm) {
1881 const double prev_t_bpm = prev_t->note_types_per_minute();
1883 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1884 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1891 return prev_t->pulse();
1894 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1895 * @param qn the position in quarter note beats.
1896 * @return the Tempo at the supplied quarter-note.
1899 TempoMap::tempo_at_quarter_note (const double& qn) const
1901 Glib::Threads::RWLock::ReaderLock lm (lock);
1903 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1906 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1907 * @param tempo the tempo.
1908 * @return the position in quarter-note beats where the map bpm
1909 * is equal to that of the Tempo. currently ignores note_type.
1912 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1914 Glib::Threads::RWLock::ReaderLock lm (lock);
1916 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1919 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1920 * @param metrics the list of metric sections used to calculate the pulse.
1921 * @param beat The BBT (meter-based) beat.
1922 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1924 * a pulse or whole note is the base musical position of a MetricSection.
1925 * it is equivalent to four quarter notes.
1929 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1931 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1933 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1936 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1937 * @param metrics the list of metric sections used to calculate the beat.
1938 * @param pulse the whole-note pulse.
1939 * @return the meter-based beat at the supplied whole-note pulse.
1941 * a pulse or whole note is the base musical position of a MetricSection.
1942 * it is equivalent to four quarter notes.
1945 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1947 MeterSection* prev_m = 0;
1949 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1951 if (!(*i)->is_tempo()) {
1952 m = static_cast<MeterSection*> (*i);
1953 if (prev_m && m->pulse() > pulse) {
1961 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1965 /* tempo section based */
1967 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1969 /* HOLD (at least) THE READER LOCK */
1970 TempoSection* prev_t = 0;
1972 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1974 if ((*i)->is_tempo()) {
1975 t = static_cast<TempoSection*> (*i);
1979 if (prev_t && t->minute() > minute) {
1980 /*the previous ts is the one containing the frame */
1981 const double ret = prev_t->pulse_at_minute (minute);
1982 /* audio locked section in new meter*/
1983 if (t->pulse() < ret) {
1992 /* treated as constant for this ts */
1993 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1995 return pulses_in_section + prev_t->pulse();
1998 /* tempo section based */
2000 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2002 /* HOLD THE READER LOCK */
2004 const TempoSection* prev_t = 0;
2006 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2009 if ((*i)->is_tempo()) {
2010 t = static_cast<TempoSection*> (*i);
2014 if (prev_t && t->pulse() > pulse) {
2015 return prev_t->minute_at_pulse (pulse);
2021 /* must be treated as constant, irrespective of _type */
2022 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
2024 return dtime + prev_t->minute();
2027 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
2028 * @param bbt The BBT time (meter-based).
2029 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2033 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2035 Glib::Threads::RWLock::ReaderLock lm (lock);
2036 return beat_at_bbt_locked (_metrics, bbt);
2041 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2043 /* CALLER HOLDS READ LOCK */
2045 MeterSection* prev_m = 0;
2047 /* because audio-locked meters have 'fake' integral beats,
2048 there is no pulse offset here.
2052 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2053 if (!(*i)->is_tempo()) {
2054 m = static_cast<MeterSection*> (*i);
2056 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2057 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2065 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2066 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2067 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2072 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2073 * @param beat The BBT (meter-based) beat.
2074 * @return The BBT time (meter-based) at the supplied meter-based beat.
2078 TempoMap::bbt_at_beat (const double& beat)
2080 Glib::Threads::RWLock::ReaderLock lm (lock);
2081 return bbt_at_beat_locked (_metrics, beat);
2085 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2087 /* CALLER HOLDS READ LOCK */
2088 MeterSection* prev_m = 0;
2089 const double beats = max (0.0, b);
2091 MeterSection* m = 0;
2093 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2094 if (!(*i)->is_tempo()) {
2095 m = static_cast<MeterSection*> (*i);
2097 if (m->beat() > beats) {
2098 /* this is the meter after the one our beat is on*/
2108 const double beats_in_ms = beats - prev_m->beat();
2109 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2110 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2111 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2112 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2116 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2117 ret.beats = (uint32_t) floor (remaining_beats);
2118 ret.bars = total_bars;
2120 /* 0 0 0 to 1 1 0 - based mapping*/
2124 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2126 ret.ticks -= BBT_Time::ticks_per_beat;
2129 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2137 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2138 * @param bbt The BBT time (meter-based).
2139 * @return the quarter note beat at the supplied BBT time
2141 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2143 * while the input uses meter, the output does not.
2146 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2148 Glib::Threads::RWLock::ReaderLock lm (lock);
2150 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2154 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2156 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2159 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2162 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2166 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2168 /* CALLER HOLDS READ LOCK */
2170 MeterSection* prev_m = 0;
2172 /* because audio-locked meters have 'fake' integral beats,
2173 there is no pulse offset here.
2177 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2178 if (!(*i)->is_tempo()) {
2179 m = static_cast<MeterSection*> (*i);
2181 if (m->bbt().bars > bbt.bars) {
2189 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2190 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2191 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2196 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2197 * @param qn the quarter-note beat.
2198 * @return The BBT time (meter-based) at the supplied meter-based beat.
2200 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2204 TempoMap::bbt_at_quarter_note (const double& qn)
2206 Glib::Threads::RWLock::ReaderLock lm (lock);
2208 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2211 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2212 * @param metrics The list of metric sections used to determine the result.
2213 * @param pulse The whole-note pulse.
2214 * @return The BBT time at the supplied whole-note pulse.
2216 * a pulse or whole note is the basic musical position of a MetricSection.
2217 * it is equivalent to four quarter notes.
2218 * while the output uses meter, the input does not.
2221 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2223 MeterSection* prev_m = 0;
2225 MeterSection* m = 0;
2227 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2229 if (!(*i)->is_tempo()) {
2230 m = static_cast<MeterSection*> (*i);
2233 double const pulses_to_m = m->pulse() - prev_m->pulse();
2234 if (prev_m->pulse() + pulses_to_m > pulse) {
2235 /* this is the meter after the one our beat is on*/
2246 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2247 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2248 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2249 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2250 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2254 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2255 ret.beats = (uint32_t) floor (remaining_beats);
2256 ret.bars = total_bars;
2258 /* 0 0 0 to 1 1 0 mapping*/
2262 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2264 ret.ticks -= BBT_Time::ticks_per_beat;
2267 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2275 /** Returns the BBT time corresponding to the supplied frame position.
2276 * @param frame the position in audio samples.
2277 * @return the BBT time at the frame position .
2281 TempoMap::bbt_at_frame (framepos_t frame)
2289 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2294 const double minute = minute_at_frame (frame);
2296 Glib::Threads::RWLock::ReaderLock lm (lock);
2298 return bbt_at_minute_locked (_metrics, minute);
2302 TempoMap::bbt_at_frame_rt (framepos_t frame)
2304 const double minute = minute_at_frame (frame);
2306 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2309 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2312 return bbt_at_minute_locked (_metrics, minute);
2316 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2326 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2327 MeterSection* prev_m = 0;
2328 MeterSection* next_m = 0;
2332 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2333 if (!(*i)->is_tempo()) {
2334 m = static_cast<MeterSection*> (*i);
2335 if (prev_m && m->minute() > minute) {
2343 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2345 /* handle frame before first meter */
2346 if (minute < prev_m->minute()) {
2349 /* audio locked meters fake their beat */
2350 if (next_m && next_m->beat() < beat) {
2351 beat = next_m->beat();
2354 beat = max (0.0, beat);
2356 const double beats_in_ms = beat - prev_m->beat();
2357 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2358 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2359 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2360 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2364 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2365 ret.beats = (uint32_t) floor (remaining_beats);
2366 ret.bars = total_bars;
2368 /* 0 0 0 to 1 1 0 - based mapping*/
2372 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2374 ret.ticks -= BBT_Time::ticks_per_beat;
2377 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2385 /** Returns the frame position corresponding to the supplied BBT time.
2386 * @param bbt the position in BBT time.
2387 * @return the frame position at bbt.
2391 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2395 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2400 if (bbt.beats < 1) {
2401 throw std::logic_error ("beats are counted from one");
2406 Glib::Threads::RWLock::ReaderLock lm (lock);
2407 minute = minute_at_bbt_locked (_metrics, bbt);
2410 return frame_at_minute (minute);
2413 /* meter & tempo section based */
2415 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2417 /* HOLD THE READER LOCK */
2419 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2424 * Returns the quarter-note beat position corresponding to the supplied frame.
2426 * @param frame the position in frames.
2427 * @return The quarter-note position of the supplied frame. Ignores meter.
2431 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2433 const double minute = minute_at_frame (frame);
2435 Glib::Threads::RWLock::ReaderLock lm (lock);
2437 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2441 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2443 const double minute = minute_at_frame (frame);
2445 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2448 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2451 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2455 * Returns the frame position corresponding to the supplied quarter-note beat.
2457 * @param quarter_note the quarter-note position.
2458 * @return the frame position of the supplied quarter-note. Ignores meter.
2463 TempoMap::frame_at_quarter_note (const double quarter_note) const
2467 Glib::Threads::RWLock::ReaderLock lm (lock);
2469 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2472 return frame_at_minute (minute);
2475 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2476 * @param beat The BBT (meter-based) beat.
2477 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2479 * a quarter-note may be compared with and assigned to Evoral::Beats.
2483 TempoMap::quarter_note_at_beat (const double beat) const
2485 Glib::Threads::RWLock::ReaderLock lm (lock);
2487 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2490 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2491 * @param quarter_note The position in quarter-note beats.
2492 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2494 * a quarter-note is the musical unit of Evoral::Beats.
2498 TempoMap::beat_at_quarter_note (const double quarter_note) const
2500 Glib::Threads::RWLock::ReaderLock lm (lock);
2502 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2505 /** Returns the duration in frames between two supplied quarter-note beat positions.
2506 * @param start the first position in quarter-note beats.
2507 * @param end the end position in quarter-note beats.
2508 * @return the frame distance ober the quarter-note beats duration.
2510 * use this rather than e.g.
2511 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2512 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2516 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2521 Glib::Threads::RWLock::ReaderLock lm (lock);
2522 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2525 return frame_at_minute (minutes);
2529 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2532 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2536 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2538 Glib::Threads::RWLock::ReaderLock lm (lock);
2540 return quarter_notes_between_frames_locked (_metrics, start, end);
2544 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2546 const TempoSection* prev_t = 0;
2548 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2551 if ((*i)->is_tempo()) {
2552 t = static_cast<TempoSection*> (*i);
2556 if (prev_t && t->frame() > start) {
2563 const double start_qn = prev_t->pulse_at_frame (start);
2565 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2568 if ((*i)->is_tempo()) {
2569 t = static_cast<TempoSection*> (*i);
2573 if (prev_t && t->frame() > end) {
2579 const double end_qn = prev_t->pulse_at_frame (end);
2581 return (end_qn - start_qn) * 4.0;
2585 TempoMap::check_solved (const Metrics& metrics) const
2587 TempoSection* prev_t = 0;
2588 MeterSection* prev_m = 0;
2590 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2593 if ((*i)->is_tempo()) {
2594 t = static_cast<TempoSection*> (*i);
2599 /* check ordering */
2600 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2604 /* precision check ensures tempo and frames align.*/
2605 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2606 if (!t->locked_to_meter()) {
2611 /* gradient limit - who knows what it should be?
2612 things are also ok (if a little chaotic) without this
2614 if (fabs (prev_t->c()) > 1000.0) {
2615 //std::cout << "c : " << prev_t->c() << std::endl;
2622 if (!(*i)->is_tempo()) {
2623 m = static_cast<MeterSection*> (*i);
2624 if (prev_m && m->position_lock_style() == AudioTime) {
2625 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2626 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2627 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2629 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2643 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2645 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2647 if ((*i)->is_tempo()) {
2648 t = static_cast<TempoSection*> (*i);
2649 if (t->locked_to_meter()) {
2650 t->set_active (true);
2651 } else if (t->position_lock_style() == AudioTime) {
2652 if (t->frame() < frame) {
2653 t->set_active (false);
2654 t->set_pulse (-1.0);
2655 } else if (t->frame() > frame) {
2656 t->set_active (true);
2657 } else if (t->frame() == frame) {
2667 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2669 TempoSection* prev_t = 0;
2670 TempoSection* section_prev = 0;
2671 double first_m_minute = 0.0;
2672 const bool sml = section->locked_to_meter();
2674 /* can't move a tempo before the first meter */
2675 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2677 if (!(*i)->is_tempo()) {
2678 m = static_cast<MeterSection*> (*i);
2680 first_m_minute = m->minute();
2685 if (!section->initial() && minute <= first_m_minute) {
2689 section->set_active (true);
2690 section->set_minute (minute);
2692 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2694 if ((*i)->is_tempo()) {
2695 t = static_cast<TempoSection*> (*i);
2707 if (t->frame() == frame_at_minute (minute)) {
2711 const bool tlm = t->position_lock_style() == MusicTime;
2713 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2714 section_prev = prev_t;
2716 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2717 if (!section->locked_to_meter()) {
2718 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2723 if (t->position_lock_style() == MusicTime) {
2724 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2725 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2727 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2728 if (!t->locked_to_meter()) {
2729 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2738 recompute_tempi (imaginary);
2740 if (check_solved (imaginary)) {
2743 dunp (imaginary, std::cout);
2747 MetricSectionFrameSorter fcmp;
2748 imaginary.sort (fcmp);
2750 recompute_tempi (imaginary);
2752 if (check_solved (imaginary)) {
2760 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2762 TempoSection* prev_t = 0;
2763 TempoSection* section_prev = 0;
2765 section->set_pulse (pulse);
2767 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2769 if ((*i)->is_tempo()) {
2770 t = static_cast<TempoSection*> (*i);
2781 section_prev = prev_t;
2785 if (t->position_lock_style() == MusicTime) {
2786 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2787 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2789 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2790 if (!t->locked_to_meter()) {
2791 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2800 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2801 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2805 recompute_tempi (imaginary);
2807 if (check_solved (imaginary)) {
2810 dunp (imaginary, std::cout);
2814 MetricSectionSorter cmp;
2815 imaginary.sort (cmp);
2817 recompute_tempi (imaginary);
2819 * XX need a restriction here, but only for this case,
2820 * as audio locked tempos don't interact in the same way.
2822 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2824 * |50 bpm |250 bpm |60 bpm
2825 * drag 250 to the pulse after 60->
2826 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2828 if (check_solved (imaginary)) {
2836 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2838 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2839 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2840 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2844 if (section->initial()) {
2845 /* lock the first tempo to our first meter */
2846 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2851 TempoSection* meter_locked_tempo = 0;
2853 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2855 if ((*ii)->is_tempo()) {
2856 t = static_cast<TempoSection*> (*ii);
2857 if (t->locked_to_meter() && t->frame() == section->frame()) {
2858 meter_locked_tempo = t;
2864 if (!meter_locked_tempo) {
2868 MeterSection* prev_m = 0;
2870 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2871 bool solved = false;
2873 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2875 if (!(*i)->is_tempo()) {
2876 m = static_cast<MeterSection*> (*i);
2878 if (prev_m && !section->initial()) {
2879 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2880 if (beats + prev_m->beat() < section->beat()) {
2881 /* set the section pulse according to its musical position,
2882 * as an earlier time than this has been requested.
2884 const double new_pulse = ((section->beat() - prev_m->beat())
2885 / prev_m->note_divisor()) + prev_m->pulse();
2887 tempo_copy->set_position_lock_style (MusicTime);
2888 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2889 meter_locked_tempo->set_position_lock_style (MusicTime);
2890 section->set_position_lock_style (MusicTime);
2891 section->set_pulse (new_pulse);
2892 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2893 meter_locked_tempo->set_position_lock_style (AudioTime);
2894 section->set_position_lock_style (AudioTime);
2895 section->set_minute (meter_locked_tempo->minute());
2901 Metrics::const_iterator d = future_map.begin();
2902 while (d != future_map.end()) {
2911 /* all is ok. set section's locked tempo if allowed.
2912 possibly disallow if there is an adjacent audio-locked tempo.
2913 XX this check could possibly go. its never actually happened here.
2915 MeterSection* meter_copy = const_cast<MeterSection*>
2916 (&meter_section_at_minute_locked (future_map, section->minute()));
2918 meter_copy->set_minute (minute);
2920 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2921 section->set_minute (minute);
2922 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2923 / prev_m->note_divisor()) + prev_m->pulse());
2924 solve_map_minute (imaginary, meter_locked_tempo, minute);
2929 Metrics::const_iterator d = future_map.begin();
2930 while (d != future_map.end()) {
2940 /* initial (first meter atm) */
2942 tempo_copy->set_minute (minute);
2943 tempo_copy->set_pulse (0.0);
2945 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2946 section->set_minute (minute);
2947 meter_locked_tempo->set_minute (minute);
2948 meter_locked_tempo->set_pulse (0.0);
2949 solve_map_minute (imaginary, meter_locked_tempo, minute);
2954 Metrics::const_iterator d = future_map.begin();
2955 while (d != future_map.end()) {
2964 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2965 section->set_beat (b_bbt);
2966 section->set_pulse (0.0);
2976 MetricSectionFrameSorter fcmp;
2977 imaginary.sort (fcmp);
2979 recompute_meters (imaginary);
2985 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2987 /* disallow setting section to an existing meter's bbt */
2988 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2990 if (!(*i)->is_tempo()) {
2991 m = static_cast<MeterSection*> (*i);
2992 if (m != section && m->bbt().bars == when.bars) {
2998 MeterSection* prev_m = 0;
2999 MeterSection* section_prev = 0;
3001 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3003 if (!(*i)->is_tempo()) {
3004 m = static_cast<MeterSection*> (*i);
3010 pair<double, BBT_Time> b_bbt;
3011 double new_pulse = 0.0;
3013 if (prev_m && m->bbt().bars > when.bars && !section_prev){
3014 section_prev = prev_m;
3016 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
3017 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
3018 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
3020 section->set_beat (b_bbt);
3021 section->set_pulse (pulse);
3022 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3026 if (m->position_lock_style() == AudioTime) {
3027 TempoSection* meter_locked_tempo = 0;
3029 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
3031 if ((*ii)->is_tempo()) {
3032 t = static_cast<TempoSection*> (*ii);
3033 if (t->locked_to_meter() && t->frame() == m->frame()) {
3034 meter_locked_tempo = t;
3040 if (!meter_locked_tempo) {
3045 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3047 if (beats + prev_m->beat() != m->beat()) {
3048 /* tempo/ meter change caused a change in beat (bar). */
3050 /* the user has requested that the previous section of music overlaps this one.
3051 we have no choice but to change the bar number here, as being locked to audio means
3052 we must stay where we are on the timeline.
3054 beats = m->beat() - prev_m->beat();
3055 b_bbt = make_pair (beats + prev_m->beat()
3056 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3057 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3059 } else if (!m->initial()) {
3060 b_bbt = make_pair (m->beat(), m->bbt());
3061 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3064 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3067 meter_locked_tempo->set_pulse (new_pulse);
3068 m->set_beat (b_bbt);
3069 m->set_pulse (new_pulse);
3073 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3074 if (beats + prev_m->beat() != m->beat()) {
3075 /* tempo/ meter change caused a change in beat (bar). */
3076 b_bbt = make_pair (beats + prev_m->beat()
3077 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3079 b_bbt = make_pair (beats + prev_m->beat()
3082 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3083 m->set_beat (b_bbt);
3084 m->set_pulse (new_pulse);
3085 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3092 if (!section_prev) {
3094 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3095 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3096 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3098 section->set_beat (b_bbt);
3099 section->set_pulse (pulse);
3100 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3103 MetricSectionSorter cmp;
3104 imaginary.sort (cmp);
3106 recompute_meters (imaginary);
3111 /** places a copy of _metrics into copy and returns a pointer
3112 * to section's equivalent in copy.
3115 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3117 TempoSection* ret = 0;
3119 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3122 if ((*i)->is_tempo()) {
3123 t = static_cast<TempoSection*> (*i);
3125 ret = new TempoSection (*t);
3126 copy.push_back (ret);
3130 TempoSection* cp = new TempoSection (*t);
3131 copy.push_back (cp);
3133 if (!(*i)->is_tempo()) {
3134 m = static_cast<MeterSection *> (*i);
3135 MeterSection* cp = new MeterSection (*m);
3136 copy.push_back (cp);
3144 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3146 MeterSection* ret = 0;
3148 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3151 if ((*i)->is_tempo()) {
3152 t = static_cast<TempoSection*> (*i);
3153 TempoSection* cp = new TempoSection (*t);
3154 copy.push_back (cp);
3157 if (!(*i)->is_tempo()) {
3158 m = static_cast<MeterSection *> (*i);
3160 ret = new MeterSection (*m);
3161 copy.push_back (ret);
3164 MeterSection* cp = new MeterSection (*m);
3165 copy.push_back (cp);
3172 /** answers the question "is this a valid beat position for this tempo section?".
3173 * it returns true if the tempo section can be moved to the requested bbt position,
3174 * leaving the tempo map in a solved state.
3175 * @param ts the tempo section to be moved
3176 * @param bbt the requested new position for the tempo section
3177 * @return true if the tempo section can be moved to the position, otherwise false.
3180 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3183 TempoSection* tempo_copy = 0;
3186 Glib::Threads::RWLock::ReaderLock lm (lock);
3187 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3193 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3195 Metrics::const_iterator d = copy.begin();
3196 while (d != copy.end()) {
3205 * 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,
3206 * taking any possible reordering as a consequence of this into account.
3207 * @param section - the section to be altered
3208 * @param bbt - the BBT time where the altered tempo will fall
3209 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3211 pair<double, framepos_t>
3212 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3215 pair<double, framepos_t> ret = make_pair (0.0, 0);
3217 Glib::Threads::RWLock::ReaderLock lm (lock);
3219 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3221 const double beat = beat_at_bbt_locked (future_map, bbt);
3223 if (section->position_lock_style() == AudioTime) {
3224 tempo_copy->set_position_lock_style (MusicTime);
3227 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3228 ret.first = tempo_copy->pulse();
3229 ret.second = tempo_copy->frame();
3231 ret.first = section->pulse();
3232 ret.second = section->frame();
3235 Metrics::const_iterator d = future_map.begin();
3236 while (d != future_map.end()) {
3243 /** moves a TempoSection to a specified position.
3244 * @param ts - the section to be moved
3245 * @param frame - the new position in frames for the tempo
3246 * @param sub_num - the snap division to use if using musical time.
3248 * if sub_num is non-zero, the frame position is used to calculate an exact
3251 * -1 | snap to bars (meter-based)
3252 * 0 | no snap - use audio frame for musical position
3253 * 1 | snap to meter-based (BBT) beat
3254 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3256 * this follows the snap convention in the gui.
3257 * if sub_num is zero, the musical position will be taken from the supplied frame.
3260 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3264 if (ts->position_lock_style() == MusicTime) {
3266 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3267 Glib::Threads::RWLock::WriterLock lm (lock);
3268 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3270 tempo_copy->set_position_lock_style (AudioTime);
3272 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3273 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3274 const double pulse = pulse_at_beat_locked (future_map, beat);
3276 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3277 solve_map_pulse (_metrics, ts, pulse);
3278 recompute_meters (_metrics);
3286 Glib::Threads::RWLock::WriterLock lm (lock);
3287 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3289 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3291 /* We're moving the object that defines the grid while snapping to it...
3292 * Placing the ts at the beat corresponding to the requested frame may shift the
3293 * grid in such a way that the mouse is left hovering over a completerly different division,
3294 * causing jittering when the mouse next moves (esp. large tempo deltas).
3296 * This alters the snap behaviour slightly in that we snap to beat divisions
3297 * in the future map rather than the existing one.
3299 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3300 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3302 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3303 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3304 ts->set_pulse (qn / 4.0);
3305 recompute_meters (_metrics);
3308 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3309 recompute_meters (_metrics);
3315 Metrics::const_iterator d = future_map.begin();
3316 while (d != future_map.end()) {
3321 MetricPositionChanged (PropertyChange ()); // Emit Signal
3324 /** moves a MeterSection to a specified position.
3325 * @param ms - the section to be moved
3326 * @param frame - the new position in frames for the meter
3328 * as a meter cannot snap to anything but bars,
3329 * the supplied frame is rounded to the nearest bar, possibly
3330 * leaving the meter position unchanged.
3333 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3337 if (ms->position_lock_style() == AudioTime) {
3340 Glib::Threads::RWLock::WriterLock lm (lock);
3341 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3343 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3344 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3345 recompute_tempi (_metrics);
3350 Glib::Threads::RWLock::WriterLock lm (lock);
3351 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3353 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3354 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3356 if (solve_map_bbt (future_map, copy, bbt)) {
3357 solve_map_bbt (_metrics, ms, bbt);
3358 recompute_tempi (_metrics);
3363 Metrics::const_iterator d = future_map.begin();
3364 while (d != future_map.end()) {
3369 MetricPositionChanged (PropertyChange ()); // Emit Signal
3373 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm, bool change_end)
3376 bool can_solve = false;
3378 Glib::Threads::RWLock::WriterLock lm (lock);
3379 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3381 if (change_end && tempo_copy->type() == TempoSection::Constant) {
3382 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3383 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3384 } else if (change_end) {
3385 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3387 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3390 recompute_tempi (future_map);
3392 if (check_solved (future_map)) {
3393 if (change_end && ts->type() == TempoSection::Constant) {
3394 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3395 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3396 } else if (change_end) {
3397 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3399 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3402 recompute_map (_metrics);
3407 Metrics::const_iterator d = future_map.begin();
3408 while (d != future_map.end()) {
3413 MetricPositionChanged (PropertyChange ()); // Emit Signal
3420 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3423 Ts (future prev_t) Tnext
3426 |----------|----------
3433 Glib::Threads::RWLock::WriterLock lm (lock);
3439 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3445 /* minimum allowed measurement distance in frames */
3446 framepos_t const min_dframe = 2;
3450 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3452 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3453 / (double) (end_frame - prev_t->frame()));
3455 new_bpm = prev_t->note_types_per_minute();
3458 std::cout << "new bpm : " << new_bpm << std::endl;
3459 new_bpm = min (new_bpm, (double) 1000.0);
3461 /* don't clamp and proceed here.
3462 testing has revealed that this can go negative,
3463 which is an entirely different thing to just being too low.
3466 if (new_bpm < 0.5) {
3470 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3471 prev_t->set_note_types_per_minute (new_bpm);
3473 prev_t->set_end_note_types_per_minute (new_bpm);
3474 prev_t->set_note_types_per_minute (new_bpm);
3477 recompute_tempi (future_map);
3478 recompute_meters (future_map);
3480 if (check_solved (future_map)) {
3481 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3482 ts->set_note_types_per_minute (new_bpm);
3484 ts->set_end_note_types_per_minute (new_bpm);
3485 ts->set_note_types_per_minute (new_bpm);
3487 recompute_tempi (_metrics);
3488 recompute_meters (_metrics);
3492 MetricPositionChanged (PropertyChange ()); // Emit Signal
3495 Metrics::const_iterator d = future_map.begin();
3496 while (d != future_map.end()) {
3503 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3506 Ts (future prev_t) Tnext
3509 |----------|----------
3516 Glib::Threads::RWLock::WriterLock lm (lock);
3522 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3525 TempoSection* next_t = 0;
3526 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3527 if ((*i)->is_tempo() && (*i)->minute() > prev_t->minute()) {
3528 next_t = static_cast<TempoSection*> (*i);
3542 /* minimum allowed measurement distance in frames */
3543 framepos_t const min_dframe = 2;
3546 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3547 new_bpm = prev_t->end_note_types_per_minute() * ((frame - prev_t->frame())
3548 / (double) (end_frame - prev_t->frame()));
3550 new_bpm = prev_t->end_note_types_per_minute();
3553 new_bpm = min (new_bpm, (double) 1000.0);
3555 if (new_bpm < 0.5) {
3559 prev_t->set_end_note_types_per_minute (new_bpm);
3561 recompute_tempi (future_map);
3562 recompute_meters (future_map);
3564 if (check_solved (future_map)) {
3565 ts->set_end_note_types_per_minute (new_bpm);
3567 recompute_tempi (_metrics);
3568 recompute_meters (_metrics);
3572 MetricPositionChanged (PropertyChange ()); // Emit Signal
3575 Metrics::const_iterator d = future_map.begin();
3576 while (d != future_map.end()) {
3583 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3585 TempoSection* next_t = 0;
3586 TempoSection* next_to_next_t = 0;
3588 bool can_solve = false;
3590 /* minimum allowed measurement distance in frames */
3591 framepos_t const min_dframe = 2;
3594 Glib::Threads::RWLock::WriterLock lm (lock);
3599 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3600 TempoSection* prev_to_prev_t = 0;
3601 const frameoffset_t fr_off = end_frame - frame;
3607 if (tempo_copy->pulse() > 0.0) {
3608 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3611 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3612 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3613 next_t = static_cast<TempoSection*> (*i);
3622 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3623 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3624 next_to_next_t = static_cast<TempoSection*> (*i);
3629 if (!next_to_next_t) {
3630 std::cout << "no next to next t" << std::endl;
3634 double prev_contribution = 0.0;
3636 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3637 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3640 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3643 framepos_t old_tc_pos = tempo_copy->frame();
3644 framepos_t old_next_pos = next_t->frame();
3645 double old_next_minute = next_t->minute();
3646 framepos_t old_next_to_next_pos = next_to_next_t->frame();
3647 double old_next_to_next_minute = next_to_next_t->minute();
3650 double new_next_bpm;
3651 double new_copy_end_bpm;
3653 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3654 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3655 / (double) (end_frame - tempo_copy->frame()));
3657 new_bpm = tempo_copy->note_types_per_minute();
3660 /* don't clamp and proceed here.
3661 testing has revealed that this can go negative,
3662 which is an entirely different thing to just being too low.
3664 if (new_bpm < 0.5) {
3668 new_bpm = min (new_bpm, (double) 1000.0);
3670 tempo_copy->set_note_types_per_minute (new_bpm);
3671 if (tempo_copy->type() == TempoSection::Constant) {
3672 tempo_copy->set_end_note_types_per_minute (new_bpm);
3674 recompute_tempi (future_map);
3676 if (check_solved (future_map)) {
3681 ts->set_note_types_per_minute (new_bpm);
3682 if (ts->type() == TempoSection::Constant) {
3683 ts->set_end_note_types_per_minute (new_bpm);
3685 recompute_map (_metrics);
3689 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3690 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3692 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3693 / (double) ((old_next_to_next_minute) - old_next_minute));
3696 new_next_bpm = next_t->note_types_per_minute();
3699 next_t->set_note_types_per_minute (new_next_bpm);
3700 recompute_tempi (future_map);
3702 if (check_solved (future_map)) {
3703 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3704 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3705 next_t = static_cast<TempoSection*> (*i);
3713 next_t->set_note_types_per_minute (new_next_bpm);
3714 recompute_map (_metrics);
3718 double next_frame_ratio = 1.0;
3719 double copy_frame_ratio = 1.0;
3721 if (next_to_next_t) {
3722 next_frame_ratio = (next_to_next_t->frame() - old_next_pos) / (double) (old_next_to_next_pos - old_next_pos);
3724 copy_frame_ratio = ((old_tc_pos - next_t->frame()) / (double) (old_tc_pos - old_next_pos));
3727 //next_frame_ratio = (((next_to_next_pos - fr_off) - next_t->frame()) / (double) ((next_to_next_pos) - next_t->frame()));
3728 //next_pulse_ratio = (start_pulse / end_pulse);
3731 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3732 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3734 next_t->set_note_types_per_minute (new_next_bpm);
3735 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3736 recompute_tempi (future_map);
3738 if (check_solved (future_map)) {
3739 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3740 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3741 next_t = static_cast<TempoSection*> (*i);
3749 next_t->set_note_types_per_minute (new_next_bpm);
3750 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3751 recompute_map (_metrics);
3757 Metrics::const_iterator d = future_map.begin();
3758 while (d != future_map.end()) {
3763 MetricPositionChanged (PropertyChange ()); // Emit Signal
3768 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3769 * the supplied frame, possibly returning a negative value.
3771 * @param frame The session frame position.
3772 * @param sub_num The subdivision to use when rounding the beat.
3773 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3774 * Positive integers indicate quarter note (non BBT) divisions.
3775 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3776 * @return The beat position of the supplied frame.
3778 * when working to a musical grid, the use of sub_nom indicates that
3779 * the position should be interpreted musically.
3781 * it effectively snaps to meter bars, meter beats or quarter note divisions
3782 * (as per current gui convention) and returns a musical position independent of frame rate.
3784 * If the supplied frame lies before the first meter, the return will be negative,
3785 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3786 * the continuation of the tempo curve (backwards).
3788 * This function is sensitive to tempo and meter.
3791 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3793 Glib::Threads::RWLock::ReaderLock lm (lock);
3795 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3799 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3801 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3804 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3805 * the supplied frame, possibly returning a negative value.
3807 * @param frame The session frame position.
3808 * @param sub_num The subdivision to use when rounding the quarter note.
3809 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3810 * Positive integers indicate quarter note (non BBT) divisions.
3811 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3812 * @return The quarter note position of the supplied frame.
3814 * When working to a musical grid, the use of sub_nom indicates that
3815 * the frame position should be interpreted musically.
3817 * it effectively snaps to meter bars, meter beats or quarter note divisions
3818 * (as per current gui convention) and returns a musical position independent of frame rate.
3820 * If the supplied frame lies before the first meter, the return will be negative,
3821 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3822 * the continuation of the tempo curve (backwards).
3824 * This function is tempo-sensitive.
3827 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3829 Glib::Threads::RWLock::ReaderLock lm (lock);
3831 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3835 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3837 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3840 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3841 } else if (sub_num == 1) {
3842 /* the gui requested exact musical (BBT) beat */
3843 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3844 } else if (sub_num == -1) {
3846 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3850 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3852 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3854 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3864 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3865 * @param pos the frame position in the tempo map.
3866 * @param bbt the distance in BBT time from pos to calculate.
3867 * @param dir the rounding direction..
3868 * @return the duration in frames between pos and bbt
3871 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3873 Glib::Threads::RWLock::ReaderLock lm (lock);
3875 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3877 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3880 pos_bbt.bars += bbt.bars;
3882 pos_bbt.ticks += bbt.ticks;
3883 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3885 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3888 pos_bbt.beats += bbt.beats;
3889 if ((double) pos_bbt.beats > divisions) {
3891 pos_bbt.beats -= divisions;
3893 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3895 return pos_bbt_frame - pos;
3899 if (pos_bbt.bars <= bbt.bars) {
3902 pos_bbt.bars -= bbt.bars;
3905 if (pos_bbt.ticks < bbt.ticks) {
3906 if (pos_bbt.bars > 1) {
3907 if (pos_bbt.beats == 1) {
3909 pos_bbt.beats = divisions;
3913 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3919 pos_bbt.ticks -= bbt.ticks;
3922 if (pos_bbt.beats <= bbt.beats) {
3923 if (pos_bbt.bars > 1) {
3925 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3930 pos_bbt.beats -= bbt.beats;
3933 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3940 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3942 return round_to_type (fr, dir, Bar);
3946 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3948 return round_to_type (fr, dir, Beat);
3952 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3954 Glib::Threads::RWLock::ReaderLock lm (lock);
3955 uint32_t ticks = (uint32_t) floor (max (0.0, pulse_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat * 4.0);
3956 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3957 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3959 ticks -= beats * BBT_Time::ticks_per_beat;
3962 /* round to next (or same iff dir == RoundUpMaybe) */
3964 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3966 if (mod == 0 && dir == RoundUpMaybe) {
3967 /* right on the subdivision, which is fine, so do nothing */
3969 } else if (mod == 0) {
3970 /* right on the subdivision, so the difference is just the subdivision ticks */
3971 ticks += ticks_one_subdivisions_worth;
3974 /* not on subdivision, compute distance to next subdivision */
3976 ticks += ticks_one_subdivisions_worth - mod;
3979 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3980 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3981 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3982 // But I'm keeping it around, until we determine there are no terrible consequences.
3983 // if (ticks >= BBT_Time::ticks_per_beat) {
3984 // ticks -= BBT_Time::ticks_per_beat;
3987 } else if (dir < 0) {
3989 /* round to previous (or same iff dir == RoundDownMaybe) */
3991 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3993 if (difference == 0 && dir == RoundDownAlways) {
3994 /* right on the subdivision, but force-rounding down,
3995 so the difference is just the subdivision ticks */
3996 difference = ticks_one_subdivisions_worth;
3999 if (ticks < difference) {
4000 ticks = BBT_Time::ticks_per_beat - ticks;
4002 ticks -= difference;
4006 /* round to nearest */
4009 /* compute the distance to the previous and next subdivision */
4011 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4013 /* closer to the next subdivision, so shift forward */
4015 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4017 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4019 if (ticks > BBT_Time::ticks_per_beat) {
4021 ticks -= BBT_Time::ticks_per_beat;
4022 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4025 } else if (rem > 0) {
4027 /* closer to previous subdivision, so shift backward */
4031 /* can't go backwards past zero, so ... */
4032 return MusicFrame (0, 0);
4034 /* step back to previous beat */
4036 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4037 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4039 ticks = lrint (ticks - rem);
4040 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4043 /* on the subdivision, do nothing */
4047 MusicFrame ret (0, 0);
4048 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4049 ret.division = sub_num;
4055 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4057 Glib::Threads::RWLock::ReaderLock lm (lock);
4058 const double minute = minute_at_frame (frame);
4059 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4060 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4061 MusicFrame ret (0, 0);
4068 /* find bar previous to 'frame' */
4074 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4078 } else if (dir > 0) {
4079 /* find bar following 'frame' */
4084 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4088 /* true rounding: find nearest bar */
4089 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4092 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4094 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4096 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4097 ret.frame = next_ft;
4102 ret.frame = prev_ft;
4114 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4117 } else if (dir > 0) {
4118 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4122 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4129 return MusicFrame (0, 0);
4133 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4134 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4136 Glib::Threads::RWLock::ReaderLock lm (lock);
4137 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4139 /* although the map handles negative beats, bbt doesn't. */
4144 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4148 while (pos >= 0 && pos < upper) {
4149 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4150 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4151 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4152 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4154 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4158 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4163 bbt.bars -= bbt.bars % bar_mod;
4167 while (pos >= 0 && pos < upper) {
4168 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4169 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4170 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4171 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4172 bbt.bars += bar_mod;
4178 TempoMap::tempo_section_at_frame (framepos_t frame) const
4180 Glib::Threads::RWLock::ReaderLock lm (lock);
4182 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4186 TempoMap::tempo_section_at_frame (framepos_t frame)
4188 Glib::Threads::RWLock::ReaderLock lm (lock);
4190 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4194 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4196 TempoSection* prev = 0;
4200 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4202 if ((*i)->is_tempo()) {
4203 t = static_cast<TempoSection*> (*i);
4207 if (prev && t->minute() > minute) {
4217 abort(); /*NOTREACHED*/
4223 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4225 TempoSection* prev = 0;
4229 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4231 if ((*i)->is_tempo()) {
4232 t = static_cast<TempoSection*> (*i);
4236 if (prev && t->minute() > minute) {
4246 abort(); /*NOTREACHED*/
4252 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4254 TempoSection* prev_t = 0;
4255 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4259 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4260 if ((*i)->is_tempo()) {
4261 t = static_cast<TempoSection*> (*i);
4267 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4278 TempoMap::next_tempo_section (TempoSection* ts) const
4280 Glib::Threads::RWLock::ReaderLock lm (lock);
4282 TempoSection* prev = 0;
4284 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4286 if ((*i)->is_tempo()) {
4287 TempoSection* t = static_cast<TempoSection*> (*i);
4293 if (prev && prev == ts) {
4304 abort(); /*NOTREACHED*/
4309 /* don't use this to calculate length (the tempo is only correct for this frame).
4310 do that stuff based on the beat_at_frame and frame_at_beat api
4313 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4315 Glib::Threads::RWLock::ReaderLock lm (lock);
4317 const TempoSection* ts_at = 0;
4318 const TempoSection* ts_after = 0;
4319 Metrics::const_iterator i;
4322 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4324 if ((*i)->is_tempo()) {
4325 t = static_cast<TempoSection*> (*i);
4329 if (ts_at && (*i)->frame() > frame) {
4339 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4341 /* must be treated as constant tempo */
4342 return ts_at->frames_per_quarter_note (_frame_rate);
4346 TempoMap::meter_section_at_frame (framepos_t frame) const
4348 Glib::Threads::RWLock::ReaderLock lm (lock);
4349 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4353 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4355 Metrics::const_iterator i;
4356 MeterSection* prev = 0;
4360 for (i = metrics.begin(); i != metrics.end(); ++i) {
4362 if (!(*i)->is_tempo()) {
4363 m = static_cast<MeterSection*> (*i);
4365 if (prev && (*i)->minute() > minute) {
4375 abort(); /*NOTREACHED*/
4382 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4384 MeterSection* prev_m = 0;
4386 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4388 if (!(*i)->is_tempo()) {
4389 m = static_cast<MeterSection*> (*i);
4390 if (prev_m && m->beat() > beat) {
4401 TempoMap::meter_section_at_beat (double beat) const
4403 Glib::Threads::RWLock::ReaderLock lm (lock);
4404 return meter_section_at_beat_locked (_metrics, beat);
4408 TempoMap::meter_at_frame (framepos_t frame) const
4410 TempoMetric m (metric_at (frame));
4415 TempoMap::fix_legacy_session ()
4417 MeterSection* prev_m = 0;
4418 TempoSection* prev_t = 0;
4419 bool have_initial_t = false;
4421 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4425 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4427 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4430 m->set_minute (0.0);
4431 m->set_position_lock_style (AudioTime);
4436 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4437 + (m->bbt().beats - 1)
4438 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4440 m->set_beat (start);
4441 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4442 + (m->bbt().beats - 1)
4443 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4444 m->set_pulse (start_beat / prev_m->note_divisor());
4447 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4455 t->set_minute (0.0);
4456 t->set_position_lock_style (AudioTime);
4458 have_initial_t = true;
4463 /* some 4.x sessions have no initial (non-movable) tempo. */
4464 if (!have_initial_t) {
4465 prev_t->set_pulse (0.0);
4466 prev_t->set_minute (0.0);
4467 prev_t->set_position_lock_style (AudioTime);
4468 prev_t->set_initial (true);
4469 prev_t->set_locked_to_meter (true);
4470 have_initial_t = true;
4473 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4474 + (t->legacy_bbt().beats - 1)
4475 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4477 t->set_pulse (beat / prev_m->note_divisor());
4479 /* really shouldn't happen but.. */
4480 t->set_pulse (beat / 4.0);
4488 TempoMap::fix_legacy_end_session ()
4490 TempoSection* prev_t = 0;
4492 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4495 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4502 if (prev_t->type() == TempoSection::Ramp) {
4503 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4505 prev_t->set_end_note_types_per_minute (prev_t->note_types_per_minute());
4515 TempoMap::get_state ()
4517 Metrics::const_iterator i;
4518 XMLNode *root = new XMLNode ("TempoMap");
4521 Glib::Threads::RWLock::ReaderLock lm (lock);
4522 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4523 root->add_child_nocopy ((*i)->get_state());
4531 TempoMap::set_state (const XMLNode& node, int /*version*/)
4534 Glib::Threads::RWLock::WriterLock lm (lock);
4537 XMLNodeConstIterator niter;
4538 Metrics old_metrics (_metrics);
4541 nlist = node.children();
4543 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4544 XMLNode* child = *niter;
4546 if (child->name() == TempoSection::xml_state_node_name) {
4549 TempoSection* ts = new TempoSection (*child, _frame_rate);
4550 _metrics.push_back (ts);
4553 catch (failed_constructor& err){
4554 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4555 _metrics = old_metrics;
4556 old_metrics.clear();
4560 } else if (child->name() == MeterSection::xml_state_node_name) {
4563 MeterSection* ms = new MeterSection (*child, _frame_rate);
4564 _metrics.push_back (ms);
4567 catch (failed_constructor& err) {
4568 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4569 _metrics = old_metrics;
4570 old_metrics.clear();
4576 if (niter == nlist.end()) {
4577 MetricSectionSorter cmp;
4578 _metrics.sort (cmp);
4581 /* check for legacy sessions where bbt was the base musical unit for tempo */
4582 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4584 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4585 if (t->legacy_bbt().bars != 0) {
4586 fix_legacy_session();
4590 if (t->legacy_end()) {
4591 fix_legacy_end_session();
4599 /* check for multiple tempo/meters at the same location, which
4600 ardour2 somehow allowed.
4603 Metrics::iterator prev = _metrics.end();
4604 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4605 if (prev != _metrics.end()) {
4607 MeterSection* prev_m;
4609 TempoSection* prev_t;
4610 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4611 if (prev_m->pulse() == ms->pulse()) {
4612 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4613 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4616 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4617 if (prev_t->pulse() == ts->pulse()) {
4618 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4619 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4627 recompute_map (_metrics);
4629 Metrics::const_iterator d = old_metrics.begin();
4630 while (d != old_metrics.end()) {
4634 old_metrics.clear ();
4637 PropertyChanged (PropertyChange ());
4643 TempoMap::dump (std::ostream& o) const
4645 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4646 const MeterSection* m;
4647 const TempoSection* t;
4648 const TempoSection* prev_t = 0;
4650 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4652 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4653 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4654 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4655 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4656 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4658 o << " current start : " << t->note_types_per_minute()
4659 << " current end : " << t->end_note_types_per_minute()
4660 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4661 o << " previous : " << prev_t->note_types_per_minute()
4662 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4663 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4664 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4665 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4666 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4669 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4670 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4671 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4672 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4675 o << "------" << std::endl;
4679 TempoMap::n_tempos() const
4681 Glib::Threads::RWLock::ReaderLock lm (lock);
4684 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4685 if ((*i)->is_tempo()) {
4694 TempoMap::n_meters() const
4696 Glib::Threads::RWLock::ReaderLock lm (lock);
4699 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4700 if (!(*i)->is_tempo()) {
4709 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4711 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4712 if ((*i)->frame() >= where && !(*i)->initial ()) {
4716 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4717 gui_set_meter_position (ms, (*i)->frame() + amount);
4720 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4721 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4726 PropertyChanged (PropertyChange ());
4730 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4734 std::list<MetricSection*> metric_kill_list;
4736 TempoSection* last_tempo = NULL;
4737 MeterSection* last_meter = NULL;
4738 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4739 bool meter_after = false; // is there a meter marker likewise?
4741 Glib::Threads::RWLock::WriterLock lm (lock);
4742 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4743 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4744 metric_kill_list.push_back(*i);
4745 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4748 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4752 else if ((*i)->frame() >= where) {
4753 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4754 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4755 if ((*i)->frame() == where) {
4756 // marker was immediately after end of range
4757 tempo_after = dynamic_cast<TempoSection*> (*i);
4758 meter_after = dynamic_cast<MeterSection*> (*i);
4764 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4765 if (last_tempo && !tempo_after) {
4766 metric_kill_list.remove(last_tempo);
4767 last_tempo->set_minute (minute_at_frame (where));
4770 if (last_meter && !meter_after) {
4771 metric_kill_list.remove(last_meter);
4772 last_meter->set_minute (minute_at_frame (where));
4776 //remove all the remaining metrics
4777 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4778 _metrics.remove(*i);
4783 recompute_map (_metrics);
4786 PropertyChanged (PropertyChange ());
4790 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4791 * pos can be -ve, if required.
4794 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4796 Glib::Threads::RWLock::ReaderLock lm (lock);
4797 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4799 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4803 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4805 Glib::Threads::RWLock::ReaderLock lm (lock);
4807 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4808 pos_bbt.ticks += op.ticks;
4809 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4811 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4813 pos_bbt.beats += op.beats;
4814 /* the meter in effect will start on the bar */
4815 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();
4816 while (pos_bbt.beats >= divisions_per_bar + 1) {
4818 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4819 pos_bbt.beats -= divisions_per_bar;
4821 pos_bbt.bars += op.bars;
4823 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4826 /** Count the number of beats that are equivalent to distance when going forward,
4830 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4832 Glib::Threads::RWLock::ReaderLock lm (lock);
4834 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4838 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4844 operator<< (std::ostream& o, const Meter& m) {
4845 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4849 operator<< (std::ostream& o, const Tempo& t) {
4850 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4854 operator<< (std::ostream& o, const MetricSection& section) {
4856 o << "MetricSection @ " << section.frame() << ' ';
4858 const TempoSection* ts;
4859 const MeterSection* ms;
4861 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4862 o << *((const Tempo*) ts);
4863 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4864 o << *((const Meter*) ms);