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);
1810 const double t_bpm = t->note_types_per_minute();
1812 if (t_bpm == tempo_bpm) {
1817 const double prev_t_bpm = prev_t->note_types_per_minute();
1819 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1820 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1827 return prev_t->minute();
1831 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1833 TempoSection* prev_t = 0;
1837 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1838 if ((*i)->is_tempo()) {
1839 t = static_cast<TempoSection*> (*i);
1843 if ((prev_t) && t->pulse() > pulse) {
1844 /* t is the section past frame */
1845 return prev_t->tempo_at_pulse (pulse);
1851 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1855 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1857 TempoSection* prev_t = 0;
1858 const double tempo_bpm = tempo.note_types_per_minute();
1860 Metrics::const_iterator i;
1862 for (i = metrics.begin(); i != metrics.end(); ++i) {
1864 if ((*i)->is_tempo()) {
1865 t = static_cast<TempoSection*> (*i);
1871 const double t_bpm = t->note_types_per_minute();
1873 if (t_bpm == tempo_bpm) {
1878 const double prev_t_bpm = prev_t->note_types_per_minute();
1880 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1881 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1888 return prev_t->pulse();
1891 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1892 * @param qn the position in quarter note beats.
1893 * @return the Tempo at the supplied quarter-note.
1896 TempoMap::tempo_at_quarter_note (const double& qn) const
1898 Glib::Threads::RWLock::ReaderLock lm (lock);
1900 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1903 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1904 * @param tempo the tempo.
1905 * @return the position in quarter-note beats where the map bpm
1906 * is equal to that of the Tempo. currently ignores note_type.
1909 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1911 Glib::Threads::RWLock::ReaderLock lm (lock);
1913 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1916 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1917 * @param metrics the list of metric sections used to calculate the pulse.
1918 * @param beat The BBT (meter-based) beat.
1919 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1921 * a pulse or whole note is the base musical position of a MetricSection.
1922 * it is equivalent to four quarter notes.
1926 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1928 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1930 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1933 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1934 * @param metrics the list of metric sections used to calculate the beat.
1935 * @param pulse the whole-note pulse.
1936 * @return the meter-based beat at the supplied whole-note pulse.
1938 * a pulse or whole note is the base musical position of a MetricSection.
1939 * it is equivalent to four quarter notes.
1942 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1944 MeterSection* prev_m = 0;
1946 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1948 if (!(*i)->is_tempo()) {
1949 m = static_cast<MeterSection*> (*i);
1950 if (prev_m && m->pulse() > pulse) {
1958 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1962 /* tempo section based */
1964 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1966 /* HOLD (at least) THE READER LOCK */
1967 TempoSection* prev_t = 0;
1969 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1971 if ((*i)->is_tempo()) {
1972 t = static_cast<TempoSection*> (*i);
1976 if (prev_t && t->minute() > minute) {
1977 /*the previous ts is the one containing the frame */
1978 const double ret = prev_t->pulse_at_minute (minute);
1979 /* audio locked section in new meter*/
1980 if (t->pulse() < ret) {
1989 /* treated as constant for this ts */
1990 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1992 return pulses_in_section + prev_t->pulse();
1995 /* tempo section based */
1997 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1999 /* HOLD THE READER LOCK */
2001 const TempoSection* prev_t = 0;
2003 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2006 if ((*i)->is_tempo()) {
2007 t = static_cast<TempoSection*> (*i);
2011 if (prev_t && t->pulse() > pulse) {
2012 return prev_t->minute_at_pulse (pulse);
2018 /* must be treated as constant, irrespective of _type */
2019 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
2021 return dtime + prev_t->minute();
2024 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
2025 * @param bbt The BBT time (meter-based).
2026 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2030 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2032 Glib::Threads::RWLock::ReaderLock lm (lock);
2033 return beat_at_bbt_locked (_metrics, bbt);
2038 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2040 /* CALLER HOLDS READ LOCK */
2042 MeterSection* prev_m = 0;
2044 /* because audio-locked meters have 'fake' integral beats,
2045 there is no pulse offset here.
2049 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2050 if (!(*i)->is_tempo()) {
2051 m = static_cast<MeterSection*> (*i);
2053 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2054 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2062 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2063 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2064 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2069 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2070 * @param beat The BBT (meter-based) beat.
2071 * @return The BBT time (meter-based) at the supplied meter-based beat.
2075 TempoMap::bbt_at_beat (const double& beat)
2077 Glib::Threads::RWLock::ReaderLock lm (lock);
2078 return bbt_at_beat_locked (_metrics, beat);
2082 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2084 /* CALLER HOLDS READ LOCK */
2085 MeterSection* prev_m = 0;
2086 const double beats = max (0.0, b);
2088 MeterSection* m = 0;
2090 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2091 if (!(*i)->is_tempo()) {
2092 m = static_cast<MeterSection*> (*i);
2094 if (m->beat() > beats) {
2095 /* this is the meter after the one our beat is on*/
2105 const double beats_in_ms = beats - prev_m->beat();
2106 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2107 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2108 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2109 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2113 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2114 ret.beats = (uint32_t) floor (remaining_beats);
2115 ret.bars = total_bars;
2117 /* 0 0 0 to 1 1 0 - based mapping*/
2121 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2123 ret.ticks -= BBT_Time::ticks_per_beat;
2126 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2134 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2135 * @param bbt The BBT time (meter-based).
2136 * @return the quarter note beat at the supplied BBT time
2138 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2140 * while the input uses meter, the output does not.
2143 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2145 Glib::Threads::RWLock::ReaderLock lm (lock);
2147 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2151 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2153 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2156 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2159 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2163 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2165 /* CALLER HOLDS READ LOCK */
2167 MeterSection* prev_m = 0;
2169 /* because audio-locked meters have 'fake' integral beats,
2170 there is no pulse offset here.
2174 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2175 if (!(*i)->is_tempo()) {
2176 m = static_cast<MeterSection*> (*i);
2178 if (m->bbt().bars > bbt.bars) {
2186 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2187 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2188 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2193 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2194 * @param qn the quarter-note beat.
2195 * @return The BBT time (meter-based) at the supplied meter-based beat.
2197 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2201 TempoMap::bbt_at_quarter_note (const double& qn)
2203 Glib::Threads::RWLock::ReaderLock lm (lock);
2205 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2208 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2209 * @param metrics The list of metric sections used to determine the result.
2210 * @param pulse The whole-note pulse.
2211 * @return The BBT time at the supplied whole-note pulse.
2213 * a pulse or whole note is the basic musical position of a MetricSection.
2214 * it is equivalent to four quarter notes.
2215 * while the output uses meter, the input does not.
2218 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2220 MeterSection* prev_m = 0;
2222 MeterSection* m = 0;
2224 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2226 if (!(*i)->is_tempo()) {
2227 m = static_cast<MeterSection*> (*i);
2230 double const pulses_to_m = m->pulse() - prev_m->pulse();
2231 if (prev_m->pulse() + pulses_to_m > pulse) {
2232 /* this is the meter after the one our beat is on*/
2243 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2244 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2245 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2246 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2247 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2251 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2252 ret.beats = (uint32_t) floor (remaining_beats);
2253 ret.bars = total_bars;
2255 /* 0 0 0 to 1 1 0 mapping*/
2259 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2261 ret.ticks -= BBT_Time::ticks_per_beat;
2264 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2272 /** Returns the BBT time corresponding to the supplied frame position.
2273 * @param frame the position in audio samples.
2274 * @return the BBT time at the frame position .
2278 TempoMap::bbt_at_frame (framepos_t frame)
2286 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2291 const double minute = minute_at_frame (frame);
2293 Glib::Threads::RWLock::ReaderLock lm (lock);
2295 return bbt_at_minute_locked (_metrics, minute);
2299 TempoMap::bbt_at_frame_rt (framepos_t frame)
2301 const double minute = minute_at_frame (frame);
2303 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2306 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2309 return bbt_at_minute_locked (_metrics, minute);
2313 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2323 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2324 MeterSection* prev_m = 0;
2325 MeterSection* next_m = 0;
2329 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2330 if (!(*i)->is_tempo()) {
2331 m = static_cast<MeterSection*> (*i);
2332 if (prev_m && m->minute() > minute) {
2340 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2342 /* handle frame before first meter */
2343 if (minute < prev_m->minute()) {
2346 /* audio locked meters fake their beat */
2347 if (next_m && next_m->beat() < beat) {
2348 beat = next_m->beat();
2351 beat = max (0.0, beat);
2353 const double beats_in_ms = beat - prev_m->beat();
2354 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2355 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2356 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2357 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2361 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2362 ret.beats = (uint32_t) floor (remaining_beats);
2363 ret.bars = total_bars;
2365 /* 0 0 0 to 1 1 0 - based mapping*/
2369 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2371 ret.ticks -= BBT_Time::ticks_per_beat;
2374 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2382 /** Returns the frame position corresponding to the supplied BBT time.
2383 * @param bbt the position in BBT time.
2384 * @return the frame position at bbt.
2388 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2392 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2397 if (bbt.beats < 1) {
2398 throw std::logic_error ("beats are counted from one");
2403 Glib::Threads::RWLock::ReaderLock lm (lock);
2404 minute = minute_at_bbt_locked (_metrics, bbt);
2407 return frame_at_minute (minute);
2410 /* meter & tempo section based */
2412 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2414 /* HOLD THE READER LOCK */
2416 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2421 * Returns the quarter-note beat position corresponding to the supplied frame.
2423 * @param frame the position in frames.
2424 * @return The quarter-note position of the supplied frame. Ignores meter.
2428 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2430 const double minute = minute_at_frame (frame);
2432 Glib::Threads::RWLock::ReaderLock lm (lock);
2434 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2438 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2440 const double minute = minute_at_frame (frame);
2442 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2445 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2448 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2452 * Returns the frame position corresponding to the supplied quarter-note beat.
2454 * @param quarter_note the quarter-note position.
2455 * @return the frame position of the supplied quarter-note. Ignores meter.
2460 TempoMap::frame_at_quarter_note (const double quarter_note) const
2464 Glib::Threads::RWLock::ReaderLock lm (lock);
2466 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2469 return frame_at_minute (minute);
2472 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2473 * @param beat The BBT (meter-based) beat.
2474 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2476 * a quarter-note may be compared with and assigned to Evoral::Beats.
2480 TempoMap::quarter_note_at_beat (const double beat) const
2482 Glib::Threads::RWLock::ReaderLock lm (lock);
2484 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2487 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2488 * @param quarter_note The position in quarter-note beats.
2489 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2491 * a quarter-note is the musical unit of Evoral::Beats.
2495 TempoMap::beat_at_quarter_note (const double quarter_note) const
2497 Glib::Threads::RWLock::ReaderLock lm (lock);
2499 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2502 /** Returns the duration in frames between two supplied quarter-note beat positions.
2503 * @param start the first position in quarter-note beats.
2504 * @param end the end position in quarter-note beats.
2505 * @return the frame distance ober the quarter-note beats duration.
2507 * use this rather than e.g.
2508 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2509 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2513 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2518 Glib::Threads::RWLock::ReaderLock lm (lock);
2519 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2522 return frame_at_minute (minutes);
2526 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2529 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2533 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2535 Glib::Threads::RWLock::ReaderLock lm (lock);
2537 return quarter_notes_between_frames_locked (_metrics, start, end);
2541 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2543 const TempoSection* prev_t = 0;
2545 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2548 if ((*i)->is_tempo()) {
2549 t = static_cast<TempoSection*> (*i);
2553 if (prev_t && t->frame() > start) {
2560 const double start_qn = prev_t->pulse_at_frame (start);
2562 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2565 if ((*i)->is_tempo()) {
2566 t = static_cast<TempoSection*> (*i);
2570 if (prev_t && t->frame() > end) {
2576 const double end_qn = prev_t->pulse_at_frame (end);
2578 return (end_qn - start_qn) * 4.0;
2582 TempoMap::check_solved (const Metrics& metrics) const
2584 TempoSection* prev_t = 0;
2585 MeterSection* prev_m = 0;
2587 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2590 if ((*i)->is_tempo()) {
2591 t = static_cast<TempoSection*> (*i);
2596 /* check ordering */
2597 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2601 /* precision check ensures tempo and frames align.*/
2602 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2603 if (!t->locked_to_meter()) {
2608 /* gradient limit - who knows what it should be?
2609 things are also ok (if a little chaotic) without this
2611 if (fabs (prev_t->c()) > 1000.0) {
2612 //std::cout << "c : " << prev_t->c() << std::endl;
2619 if (!(*i)->is_tempo()) {
2620 m = static_cast<MeterSection*> (*i);
2621 if (prev_m && m->position_lock_style() == AudioTime) {
2622 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2623 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2624 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2626 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2640 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2642 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2644 if ((*i)->is_tempo()) {
2645 t = static_cast<TempoSection*> (*i);
2646 if (t->locked_to_meter()) {
2647 t->set_active (true);
2648 } else if (t->position_lock_style() == AudioTime) {
2649 if (t->frame() < frame) {
2650 t->set_active (false);
2651 t->set_pulse (-1.0);
2652 } else if (t->frame() > frame) {
2653 t->set_active (true);
2654 } else if (t->frame() == frame) {
2664 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2666 TempoSection* prev_t = 0;
2667 TempoSection* section_prev = 0;
2668 double first_m_minute = 0.0;
2669 const bool sml = section->locked_to_meter();
2671 /* can't move a tempo before the first meter */
2672 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2674 if (!(*i)->is_tempo()) {
2675 m = static_cast<MeterSection*> (*i);
2677 first_m_minute = m->minute();
2682 if (!section->initial() && minute <= first_m_minute) {
2686 section->set_active (true);
2687 section->set_minute (minute);
2689 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2691 if ((*i)->is_tempo()) {
2692 t = static_cast<TempoSection*> (*i);
2704 if (t->frame() == frame_at_minute (minute)) {
2708 const bool tlm = t->position_lock_style() == MusicTime;
2710 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2711 section_prev = prev_t;
2713 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2714 if (!section->locked_to_meter()) {
2715 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2720 if (t->position_lock_style() == MusicTime) {
2721 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2722 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2724 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2725 if (!t->locked_to_meter()) {
2726 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2735 recompute_tempi (imaginary);
2737 if (check_solved (imaginary)) {
2740 dunp (imaginary, std::cout);
2744 MetricSectionFrameSorter fcmp;
2745 imaginary.sort (fcmp);
2747 recompute_tempi (imaginary);
2749 if (check_solved (imaginary)) {
2757 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2759 TempoSection* prev_t = 0;
2760 TempoSection* section_prev = 0;
2762 section->set_pulse (pulse);
2764 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2766 if ((*i)->is_tempo()) {
2767 t = static_cast<TempoSection*> (*i);
2778 section_prev = prev_t;
2782 if (t->position_lock_style() == MusicTime) {
2783 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2784 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2786 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2787 if (!t->locked_to_meter()) {
2788 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2797 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2798 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2802 recompute_tempi (imaginary);
2804 if (check_solved (imaginary)) {
2807 dunp (imaginary, std::cout);
2811 MetricSectionSorter cmp;
2812 imaginary.sort (cmp);
2814 recompute_tempi (imaginary);
2816 * XX need a restriction here, but only for this case,
2817 * as audio locked tempos don't interact in the same way.
2819 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2821 * |50 bpm |250 bpm |60 bpm
2822 * drag 250 to the pulse after 60->
2823 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2825 if (check_solved (imaginary)) {
2833 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2835 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2836 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2837 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2841 if (section->initial()) {
2842 /* lock the first tempo to our first meter */
2843 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2848 TempoSection* meter_locked_tempo = 0;
2850 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2852 if ((*ii)->is_tempo()) {
2853 t = static_cast<TempoSection*> (*ii);
2854 if (t->locked_to_meter() && t->frame() == section->frame()) {
2855 meter_locked_tempo = t;
2861 if (!meter_locked_tempo) {
2865 MeterSection* prev_m = 0;
2867 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2868 bool solved = false;
2870 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2872 if (!(*i)->is_tempo()) {
2873 m = static_cast<MeterSection*> (*i);
2875 if (prev_m && !section->initial()) {
2876 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2877 if (beats + prev_m->beat() < section->beat()) {
2878 /* set the section pulse according to its musical position,
2879 * as an earlier time than this has been requested.
2881 const double new_pulse = ((section->beat() - prev_m->beat())
2882 / prev_m->note_divisor()) + prev_m->pulse();
2884 tempo_copy->set_position_lock_style (MusicTime);
2885 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2886 meter_locked_tempo->set_position_lock_style (MusicTime);
2887 section->set_position_lock_style (MusicTime);
2888 section->set_pulse (new_pulse);
2889 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2890 meter_locked_tempo->set_position_lock_style (AudioTime);
2891 section->set_position_lock_style (AudioTime);
2892 section->set_minute (meter_locked_tempo->minute());
2898 Metrics::const_iterator d = future_map.begin();
2899 while (d != future_map.end()) {
2908 /* all is ok. set section's locked tempo if allowed.
2909 possibly disallow if there is an adjacent audio-locked tempo.
2910 XX this check could possibly go. its never actually happened here.
2912 MeterSection* meter_copy = const_cast<MeterSection*>
2913 (&meter_section_at_minute_locked (future_map, section->minute()));
2915 meter_copy->set_minute (minute);
2917 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2918 section->set_minute (minute);
2919 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2920 / prev_m->note_divisor()) + prev_m->pulse());
2921 solve_map_minute (imaginary, meter_locked_tempo, minute);
2926 Metrics::const_iterator d = future_map.begin();
2927 while (d != future_map.end()) {
2937 /* initial (first meter atm) */
2939 tempo_copy->set_minute (minute);
2940 tempo_copy->set_pulse (0.0);
2942 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2943 section->set_minute (minute);
2944 meter_locked_tempo->set_minute (minute);
2945 meter_locked_tempo->set_pulse (0.0);
2946 solve_map_minute (imaginary, meter_locked_tempo, minute);
2951 Metrics::const_iterator d = future_map.begin();
2952 while (d != future_map.end()) {
2961 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2962 section->set_beat (b_bbt);
2963 section->set_pulse (0.0);
2973 MetricSectionFrameSorter fcmp;
2974 imaginary.sort (fcmp);
2976 recompute_meters (imaginary);
2982 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2984 /* disallow setting section to an existing meter's bbt */
2985 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2987 if (!(*i)->is_tempo()) {
2988 m = static_cast<MeterSection*> (*i);
2989 if (m != section && m->bbt().bars == when.bars) {
2995 MeterSection* prev_m = 0;
2996 MeterSection* section_prev = 0;
2998 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3000 if (!(*i)->is_tempo()) {
3001 m = static_cast<MeterSection*> (*i);
3007 pair<double, BBT_Time> b_bbt;
3008 double new_pulse = 0.0;
3010 if (prev_m && m->bbt().bars > when.bars && !section_prev){
3011 section_prev = prev_m;
3013 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
3014 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
3015 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
3017 section->set_beat (b_bbt);
3018 section->set_pulse (pulse);
3019 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3023 if (m->position_lock_style() == AudioTime) {
3024 TempoSection* meter_locked_tempo = 0;
3026 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
3028 if ((*ii)->is_tempo()) {
3029 t = static_cast<TempoSection*> (*ii);
3030 if (t->locked_to_meter() && t->frame() == m->frame()) {
3031 meter_locked_tempo = t;
3037 if (!meter_locked_tempo) {
3042 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3044 if (beats + prev_m->beat() != m->beat()) {
3045 /* tempo/ meter change caused a change in beat (bar). */
3047 /* the user has requested that the previous section of music overlaps this one.
3048 we have no choice but to change the bar number here, as being locked to audio means
3049 we must stay where we are on the timeline.
3051 beats = m->beat() - prev_m->beat();
3052 b_bbt = make_pair (beats + prev_m->beat()
3053 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3054 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3056 } else if (!m->initial()) {
3057 b_bbt = make_pair (m->beat(), m->bbt());
3058 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3061 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3064 meter_locked_tempo->set_pulse (new_pulse);
3065 m->set_beat (b_bbt);
3066 m->set_pulse (new_pulse);
3070 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3071 if (beats + prev_m->beat() != m->beat()) {
3072 /* tempo/ meter change caused a change in beat (bar). */
3073 b_bbt = make_pair (beats + prev_m->beat()
3074 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3076 b_bbt = make_pair (beats + prev_m->beat()
3079 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3080 m->set_beat (b_bbt);
3081 m->set_pulse (new_pulse);
3082 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3089 if (!section_prev) {
3091 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3092 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3093 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3095 section->set_beat (b_bbt);
3096 section->set_pulse (pulse);
3097 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3100 MetricSectionSorter cmp;
3101 imaginary.sort (cmp);
3103 recompute_meters (imaginary);
3108 /** places a copy of _metrics into copy and returns a pointer
3109 * to section's equivalent in copy.
3112 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3114 TempoSection* ret = 0;
3116 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3119 if ((*i)->is_tempo()) {
3120 t = static_cast<TempoSection*> (*i);
3122 ret = new TempoSection (*t);
3123 copy.push_back (ret);
3127 TempoSection* cp = new TempoSection (*t);
3128 copy.push_back (cp);
3130 if (!(*i)->is_tempo()) {
3131 m = static_cast<MeterSection *> (*i);
3132 MeterSection* cp = new MeterSection (*m);
3133 copy.push_back (cp);
3141 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3143 MeterSection* ret = 0;
3145 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3148 if ((*i)->is_tempo()) {
3149 t = static_cast<TempoSection*> (*i);
3150 TempoSection* cp = new TempoSection (*t);
3151 copy.push_back (cp);
3154 if (!(*i)->is_tempo()) {
3155 m = static_cast<MeterSection *> (*i);
3157 ret = new MeterSection (*m);
3158 copy.push_back (ret);
3161 MeterSection* cp = new MeterSection (*m);
3162 copy.push_back (cp);
3169 /** answers the question "is this a valid beat position for this tempo section?".
3170 * it returns true if the tempo section can be moved to the requested bbt position,
3171 * leaving the tempo map in a solved state.
3172 * @param ts the tempo section to be moved
3173 * @param bbt the requested new position for the tempo section
3174 * @return true if the tempo section can be moved to the position, otherwise false.
3177 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3180 TempoSection* tempo_copy = 0;
3183 Glib::Threads::RWLock::ReaderLock lm (lock);
3184 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3190 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3192 Metrics::const_iterator d = copy.begin();
3193 while (d != copy.end()) {
3202 * 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,
3203 * taking any possible reordering as a consequence of this into account.
3204 * @param section - the section to be altered
3205 * @param bbt - the BBT time where the altered tempo will fall
3206 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3208 pair<double, framepos_t>
3209 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3212 pair<double, framepos_t> ret = make_pair (0.0, 0);
3214 Glib::Threads::RWLock::ReaderLock lm (lock);
3216 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3218 const double beat = beat_at_bbt_locked (future_map, bbt);
3220 if (section->position_lock_style() == AudioTime) {
3221 tempo_copy->set_position_lock_style (MusicTime);
3224 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3225 ret.first = tempo_copy->pulse();
3226 ret.second = tempo_copy->frame();
3228 ret.first = section->pulse();
3229 ret.second = section->frame();
3232 Metrics::const_iterator d = future_map.begin();
3233 while (d != future_map.end()) {
3240 /** moves a TempoSection to a specified position.
3241 * @param ts - the section to be moved
3242 * @param frame - the new position in frames for the tempo
3243 * @param sub_num - the snap division to use if using musical time.
3245 * if sub_num is non-zero, the frame position is used to calculate an exact
3248 * -1 | snap to bars (meter-based)
3249 * 0 | no snap - use audio frame for musical position
3250 * 1 | snap to meter-based (BBT) beat
3251 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3253 * this follows the snap convention in the gui.
3254 * if sub_num is zero, the musical position will be taken from the supplied frame.
3257 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3261 if (ts->position_lock_style() == MusicTime) {
3263 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3264 Glib::Threads::RWLock::WriterLock lm (lock);
3265 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3267 tempo_copy->set_position_lock_style (AudioTime);
3269 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3270 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3271 const double pulse = pulse_at_beat_locked (future_map, beat);
3273 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3274 solve_map_pulse (_metrics, ts, pulse);
3275 recompute_meters (_metrics);
3283 Glib::Threads::RWLock::WriterLock lm (lock);
3284 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3286 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3288 /* We're moving the object that defines the grid while snapping to it...
3289 * Placing the ts at the beat corresponding to the requested frame may shift the
3290 * grid in such a way that the mouse is left hovering over a completerly different division,
3291 * causing jittering when the mouse next moves (esp. large tempo deltas).
3293 * This alters the snap behaviour slightly in that we snap to beat divisions
3294 * in the future map rather than the existing one.
3296 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3297 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3299 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3300 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3301 ts->set_pulse (qn / 4.0);
3302 recompute_meters (_metrics);
3305 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3306 recompute_meters (_metrics);
3312 Metrics::const_iterator d = future_map.begin();
3313 while (d != future_map.end()) {
3318 MetricPositionChanged (PropertyChange ()); // Emit Signal
3321 /** moves a MeterSection to a specified position.
3322 * @param ms - the section to be moved
3323 * @param frame - the new position in frames for the meter
3325 * as a meter cannot snap to anything but bars,
3326 * the supplied frame is rounded to the nearest bar, possibly
3327 * leaving the meter position unchanged.
3330 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3334 if (ms->position_lock_style() == AudioTime) {
3337 Glib::Threads::RWLock::WriterLock lm (lock);
3338 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3340 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3341 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3342 recompute_tempi (_metrics);
3347 Glib::Threads::RWLock::WriterLock lm (lock);
3348 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3350 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3351 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3353 if (solve_map_bbt (future_map, copy, bbt)) {
3354 solve_map_bbt (_metrics, ms, bbt);
3355 recompute_tempi (_metrics);
3360 Metrics::const_iterator d = future_map.begin();
3361 while (d != future_map.end()) {
3366 MetricPositionChanged (PropertyChange ()); // Emit Signal
3370 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm, bool change_end)
3373 bool can_solve = false;
3375 Glib::Threads::RWLock::WriterLock lm (lock);
3376 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3378 if (change_end && tempo_copy->type() == TempoSection::Constant) {
3379 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3380 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3381 } else if (change_end) {
3382 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3384 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3387 recompute_tempi (future_map);
3389 if (check_solved (future_map)) {
3390 if (change_end && ts->type() == TempoSection::Constant) {
3391 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3392 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3393 } else if (change_end) {
3394 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3396 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3399 recompute_map (_metrics);
3404 Metrics::const_iterator d = future_map.begin();
3405 while (d != future_map.end()) {
3410 MetricPositionChanged (PropertyChange ()); // Emit Signal
3417 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3420 Ts (future prev_t) Tnext
3423 |----------|----------
3430 Glib::Threads::RWLock::WriterLock lm (lock);
3436 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3442 /* minimum allowed measurement distance in frames */
3443 framepos_t const min_dframe = 2;
3447 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3449 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3450 / (double) (end_frame - prev_t->frame()));
3452 new_bpm = prev_t->note_types_per_minute();
3455 std::cout << "new bpm : " << new_bpm << std::endl;
3456 new_bpm = min (new_bpm, (double) 1000.0);
3458 /* don't clamp and proceed here.
3459 testing has revealed that this can go negative,
3460 which is an entirely different thing to just being too low.
3463 if (new_bpm < 0.5) {
3467 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3468 prev_t->set_note_types_per_minute (new_bpm);
3470 prev_t->set_end_note_types_per_minute (new_bpm);
3471 prev_t->set_note_types_per_minute (new_bpm);
3474 recompute_tempi (future_map);
3475 recompute_meters (future_map);
3477 if (check_solved (future_map)) {
3478 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3479 ts->set_note_types_per_minute (new_bpm);
3481 ts->set_end_note_types_per_minute (new_bpm);
3482 ts->set_note_types_per_minute (new_bpm);
3484 recompute_tempi (_metrics);
3485 recompute_meters (_metrics);
3489 MetricPositionChanged (PropertyChange ()); // Emit Signal
3492 Metrics::const_iterator d = future_map.begin();
3493 while (d != future_map.end()) {
3500 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3503 Ts (future prev_t) Tnext
3506 |----------|----------
3513 Glib::Threads::RWLock::WriterLock lm (lock);
3519 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3522 TempoSection* next_t = 0;
3523 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3524 if ((*i)->is_tempo() && (*i)->minute() > prev_t->minute()) {
3525 next_t = static_cast<TempoSection*> (*i);
3539 /* minimum allowed measurement distance in frames */
3540 framepos_t const min_dframe = 2;
3543 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3544 new_bpm = prev_t->end_note_types_per_minute() * ((frame - prev_t->frame())
3545 / (double) (end_frame - prev_t->frame()));
3547 new_bpm = prev_t->end_note_types_per_minute();
3550 new_bpm = min (new_bpm, (double) 1000.0);
3552 if (new_bpm < 0.5) {
3556 prev_t->set_end_note_types_per_minute (new_bpm);
3558 recompute_tempi (future_map);
3559 recompute_meters (future_map);
3561 if (check_solved (future_map)) {
3562 ts->set_end_note_types_per_minute (new_bpm);
3564 recompute_tempi (_metrics);
3565 recompute_meters (_metrics);
3569 MetricPositionChanged (PropertyChange ()); // Emit Signal
3572 Metrics::const_iterator d = future_map.begin();
3573 while (d != future_map.end()) {
3580 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3582 TempoSection* next_t = 0;
3583 TempoSection* next_to_next_t = 0;
3585 bool can_solve = false;
3587 /* minimum allowed measurement distance in frames */
3588 framepos_t const min_dframe = 2;
3591 Glib::Threads::RWLock::WriterLock lm (lock);
3596 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3597 TempoSection* prev_to_prev_t = 0;
3598 const frameoffset_t fr_off = end_frame - frame;
3604 if (tempo_copy->pulse() > 0.0) {
3605 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3608 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3609 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3610 next_t = static_cast<TempoSection*> (*i);
3619 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3620 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3621 next_to_next_t = static_cast<TempoSection*> (*i);
3626 if (!next_to_next_t) {
3627 std::cout << "no next to next t" << std::endl;
3631 double prev_contribution = 0.0;
3633 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3634 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3637 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3640 framepos_t old_tc_pos = tempo_copy->frame();
3641 framepos_t old_next_pos = next_t->frame();
3642 double old_next_minute = next_t->minute();
3643 framepos_t old_next_to_next_pos = next_to_next_t->frame();
3644 double old_next_to_next_minute = next_to_next_t->minute();
3647 double new_next_bpm;
3648 double new_copy_end_bpm;
3650 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3651 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3652 / (double) (end_frame - tempo_copy->frame()));
3654 new_bpm = tempo_copy->note_types_per_minute();
3657 /* don't clamp and proceed here.
3658 testing has revealed that this can go negative,
3659 which is an entirely different thing to just being too low.
3661 if (new_bpm < 0.5) {
3665 new_bpm = min (new_bpm, (double) 1000.0);
3667 tempo_copy->set_note_types_per_minute (new_bpm);
3668 if (tempo_copy->type() == TempoSection::Constant) {
3669 tempo_copy->set_end_note_types_per_minute (new_bpm);
3671 recompute_tempi (future_map);
3673 if (check_solved (future_map)) {
3678 ts->set_note_types_per_minute (new_bpm);
3679 if (ts->type() == TempoSection::Constant) {
3680 ts->set_end_note_types_per_minute (new_bpm);
3682 recompute_map (_metrics);
3686 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3687 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3689 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3690 / (double) ((old_next_to_next_minute) - old_next_minute));
3693 new_next_bpm = next_t->note_types_per_minute();
3696 next_t->set_note_types_per_minute (new_next_bpm);
3697 recompute_tempi (future_map);
3699 if (check_solved (future_map)) {
3700 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3701 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3702 next_t = static_cast<TempoSection*> (*i);
3710 next_t->set_note_types_per_minute (new_next_bpm);
3711 recompute_map (_metrics);
3715 double next_frame_ratio = 1.0;
3716 double copy_frame_ratio = 1.0;
3718 if (next_to_next_t) {
3719 next_frame_ratio = (next_to_next_t->frame() - old_next_pos) / (double) (old_next_to_next_pos - old_next_pos);
3721 copy_frame_ratio = ((old_tc_pos - next_t->frame()) / (double) (old_tc_pos - old_next_pos));
3724 //next_frame_ratio = (((next_to_next_pos - fr_off) - next_t->frame()) / (double) ((next_to_next_pos) - next_t->frame()));
3725 //next_pulse_ratio = (start_pulse / end_pulse);
3728 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3729 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3731 next_t->set_note_types_per_minute (new_next_bpm);
3732 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3733 recompute_tempi (future_map);
3735 if (check_solved (future_map)) {
3736 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3737 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3738 next_t = static_cast<TempoSection*> (*i);
3746 next_t->set_note_types_per_minute (new_next_bpm);
3747 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3748 recompute_map (_metrics);
3754 Metrics::const_iterator d = future_map.begin();
3755 while (d != future_map.end()) {
3760 MetricPositionChanged (PropertyChange ()); // Emit Signal
3765 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3766 * the supplied frame, possibly returning a negative value.
3768 * @param frame The session frame position.
3769 * @param sub_num The subdivision to use when rounding the beat.
3770 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3771 * Positive integers indicate quarter note (non BBT) divisions.
3772 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3773 * @return The beat position of the supplied frame.
3775 * when working to a musical grid, the use of sub_nom indicates that
3776 * the position should be interpreted musically.
3778 * it effectively snaps to meter bars, meter beats or quarter note divisions
3779 * (as per current gui convention) and returns a musical position independent of frame rate.
3781 * If the supplied frame lies before the first meter, the return will be negative,
3782 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3783 * the continuation of the tempo curve (backwards).
3785 * This function is sensitive to tempo and meter.
3788 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3790 Glib::Threads::RWLock::ReaderLock lm (lock);
3792 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3796 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3798 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3801 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3802 * the supplied frame, possibly returning a negative value.
3804 * @param frame The session frame position.
3805 * @param sub_num The subdivision to use when rounding the quarter note.
3806 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3807 * Positive integers indicate quarter note (non BBT) divisions.
3808 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3809 * @return The quarter note position of the supplied frame.
3811 * When working to a musical grid, the use of sub_nom indicates that
3812 * the frame position should be interpreted musically.
3814 * it effectively snaps to meter bars, meter beats or quarter note divisions
3815 * (as per current gui convention) and returns a musical position independent of frame rate.
3817 * If the supplied frame lies before the first meter, the return will be negative,
3818 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3819 * the continuation of the tempo curve (backwards).
3821 * This function is tempo-sensitive.
3824 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3826 Glib::Threads::RWLock::ReaderLock lm (lock);
3828 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3832 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3834 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3837 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3838 } else if (sub_num == 1) {
3839 /* the gui requested exact musical (BBT) beat */
3840 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3841 } else if (sub_num == -1) {
3843 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3847 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3849 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3851 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3861 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3862 * @param pos the frame position in the tempo map.
3863 * @param bbt the distance in BBT time from pos to calculate.
3864 * @param dir the rounding direction..
3865 * @return the duration in frames between pos and bbt
3868 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3870 Glib::Threads::RWLock::ReaderLock lm (lock);
3872 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3874 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3877 pos_bbt.bars += bbt.bars;
3879 pos_bbt.ticks += bbt.ticks;
3880 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3882 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3885 pos_bbt.beats += bbt.beats;
3886 if ((double) pos_bbt.beats > divisions) {
3888 pos_bbt.beats -= divisions;
3890 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3892 return pos_bbt_frame - pos;
3896 if (pos_bbt.bars <= bbt.bars) {
3899 pos_bbt.bars -= bbt.bars;
3902 if (pos_bbt.ticks < bbt.ticks) {
3903 if (pos_bbt.bars > 1) {
3904 if (pos_bbt.beats == 1) {
3906 pos_bbt.beats = divisions;
3910 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3916 pos_bbt.ticks -= bbt.ticks;
3919 if (pos_bbt.beats <= bbt.beats) {
3920 if (pos_bbt.bars > 1) {
3922 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3927 pos_bbt.beats -= bbt.beats;
3930 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3937 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3939 return round_to_type (fr, dir, Bar);
3943 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3945 return round_to_type (fr, dir, Beat);
3949 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3951 Glib::Threads::RWLock::ReaderLock lm (lock);
3952 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);
3953 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3954 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3956 ticks -= beats * BBT_Time::ticks_per_beat;
3959 /* round to next (or same iff dir == RoundUpMaybe) */
3961 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3963 if (mod == 0 && dir == RoundUpMaybe) {
3964 /* right on the subdivision, which is fine, so do nothing */
3966 } else if (mod == 0) {
3967 /* right on the subdivision, so the difference is just the subdivision ticks */
3968 ticks += ticks_one_subdivisions_worth;
3971 /* not on subdivision, compute distance to next subdivision */
3973 ticks += ticks_one_subdivisions_worth - mod;
3976 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3977 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3978 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3979 // But I'm keeping it around, until we determine there are no terrible consequences.
3980 // if (ticks >= BBT_Time::ticks_per_beat) {
3981 // ticks -= BBT_Time::ticks_per_beat;
3984 } else if (dir < 0) {
3986 /* round to previous (or same iff dir == RoundDownMaybe) */
3988 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3990 if (difference == 0 && dir == RoundDownAlways) {
3991 /* right on the subdivision, but force-rounding down,
3992 so the difference is just the subdivision ticks */
3993 difference = ticks_one_subdivisions_worth;
3996 if (ticks < difference) {
3997 ticks = BBT_Time::ticks_per_beat - ticks;
3999 ticks -= difference;
4003 /* round to nearest */
4006 /* compute the distance to the previous and next subdivision */
4008 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4010 /* closer to the next subdivision, so shift forward */
4012 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4014 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4016 if (ticks > BBT_Time::ticks_per_beat) {
4018 ticks -= BBT_Time::ticks_per_beat;
4019 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4022 } else if (rem > 0) {
4024 /* closer to previous subdivision, so shift backward */
4028 /* can't go backwards past zero, so ... */
4029 return MusicFrame (0, 0);
4031 /* step back to previous beat */
4033 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4034 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4036 ticks = lrint (ticks - rem);
4037 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4040 /* on the subdivision, do nothing */
4044 MusicFrame ret (0, 0);
4045 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4046 ret.division = sub_num;
4052 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4054 Glib::Threads::RWLock::ReaderLock lm (lock);
4055 const double minute = minute_at_frame (frame);
4056 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4057 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4058 MusicFrame ret (0, 0);
4065 /* find bar previous to 'frame' */
4071 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4075 } else if (dir > 0) {
4076 /* find bar following 'frame' */
4081 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4085 /* true rounding: find nearest bar */
4086 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4089 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4091 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4093 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4094 ret.frame = next_ft;
4099 ret.frame = prev_ft;
4111 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4114 } else if (dir > 0) {
4115 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4119 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4126 return MusicFrame (0, 0);
4130 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4131 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4133 Glib::Threads::RWLock::ReaderLock lm (lock);
4134 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4136 /* although the map handles negative beats, bbt doesn't. */
4141 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4145 while (pos >= 0 && pos < upper) {
4146 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4147 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4148 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4149 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4151 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4155 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4160 bbt.bars -= bbt.bars % bar_mod;
4164 while (pos >= 0 && pos < upper) {
4165 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4166 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4167 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4168 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4169 bbt.bars += bar_mod;
4175 TempoMap::tempo_section_at_frame (framepos_t frame) const
4177 Glib::Threads::RWLock::ReaderLock lm (lock);
4179 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4183 TempoMap::tempo_section_at_frame (framepos_t frame)
4185 Glib::Threads::RWLock::ReaderLock lm (lock);
4187 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4191 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4193 TempoSection* prev = 0;
4197 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4199 if ((*i)->is_tempo()) {
4200 t = static_cast<TempoSection*> (*i);
4204 if (prev && t->minute() > minute) {
4214 abort(); /*NOTREACHED*/
4220 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4222 TempoSection* prev = 0;
4226 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4228 if ((*i)->is_tempo()) {
4229 t = static_cast<TempoSection*> (*i);
4233 if (prev && t->minute() > minute) {
4243 abort(); /*NOTREACHED*/
4249 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4251 TempoSection* prev_t = 0;
4252 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4256 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4257 if ((*i)->is_tempo()) {
4258 t = static_cast<TempoSection*> (*i);
4264 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4274 /* don't use this to calculate length (the tempo is only correct for this frame).
4275 do that stuff based on the beat_at_frame and frame_at_beat api
4278 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4280 Glib::Threads::RWLock::ReaderLock lm (lock);
4282 const TempoSection* ts_at = 0;
4283 const TempoSection* ts_after = 0;
4284 Metrics::const_iterator i;
4287 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4289 if ((*i)->is_tempo()) {
4290 t = static_cast<TempoSection*> (*i);
4294 if (ts_at && (*i)->frame() > frame) {
4304 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4306 /* must be treated as constant tempo */
4307 return ts_at->frames_per_quarter_note (_frame_rate);
4311 TempoMap::meter_section_at_frame (framepos_t frame) const
4313 Glib::Threads::RWLock::ReaderLock lm (lock);
4314 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4318 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4320 Metrics::const_iterator i;
4321 MeterSection* prev = 0;
4325 for (i = metrics.begin(); i != metrics.end(); ++i) {
4327 if (!(*i)->is_tempo()) {
4328 m = static_cast<MeterSection*> (*i);
4330 if (prev && (*i)->minute() > minute) {
4340 abort(); /*NOTREACHED*/
4347 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4349 MeterSection* prev_m = 0;
4351 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4353 if (!(*i)->is_tempo()) {
4354 m = static_cast<MeterSection*> (*i);
4355 if (prev_m && m->beat() > beat) {
4366 TempoMap::meter_section_at_beat (double beat) const
4368 Glib::Threads::RWLock::ReaderLock lm (lock);
4369 return meter_section_at_beat_locked (_metrics, beat);
4373 TempoMap::meter_at_frame (framepos_t frame) const
4375 TempoMetric m (metric_at (frame));
4380 TempoMap::fix_legacy_session ()
4382 MeterSection* prev_m = 0;
4383 TempoSection* prev_t = 0;
4384 bool have_initial_t = false;
4386 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4390 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4392 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4395 m->set_minute (0.0);
4396 m->set_position_lock_style (AudioTime);
4401 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4402 + (m->bbt().beats - 1)
4403 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4405 m->set_beat (start);
4406 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4407 + (m->bbt().beats - 1)
4408 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4409 m->set_pulse (start_beat / prev_m->note_divisor());
4412 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4420 t->set_minute (0.0);
4421 t->set_position_lock_style (AudioTime);
4423 have_initial_t = true;
4428 /* some 4.x sessions have no initial (non-movable) tempo. */
4429 if (!have_initial_t) {
4430 prev_t->set_pulse (0.0);
4431 prev_t->set_minute (0.0);
4432 prev_t->set_position_lock_style (AudioTime);
4433 prev_t->set_initial (true);
4434 prev_t->set_locked_to_meter (true);
4435 have_initial_t = true;
4438 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4439 + (t->legacy_bbt().beats - 1)
4440 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4442 t->set_pulse (beat / prev_m->note_divisor());
4444 /* really shouldn't happen but.. */
4445 t->set_pulse (beat / 4.0);
4453 TempoMap::fix_legacy_end_session ()
4455 TempoSection* prev_t = 0;
4457 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4460 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4467 if (prev_t->type() == TempoSection::Ramp) {
4468 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4470 prev_t->set_end_note_types_per_minute (prev_t->note_types_per_minute());
4480 TempoMap::get_state ()
4482 Metrics::const_iterator i;
4483 XMLNode *root = new XMLNode ("TempoMap");
4486 Glib::Threads::RWLock::ReaderLock lm (lock);
4487 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4488 root->add_child_nocopy ((*i)->get_state());
4496 TempoMap::set_state (const XMLNode& node, int /*version*/)
4499 Glib::Threads::RWLock::WriterLock lm (lock);
4502 XMLNodeConstIterator niter;
4503 Metrics old_metrics (_metrics);
4506 nlist = node.children();
4508 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4509 XMLNode* child = *niter;
4511 if (child->name() == TempoSection::xml_state_node_name) {
4514 TempoSection* ts = new TempoSection (*child, _frame_rate);
4515 _metrics.push_back (ts);
4518 catch (failed_constructor& err){
4519 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4520 _metrics = old_metrics;
4521 old_metrics.clear();
4525 } else if (child->name() == MeterSection::xml_state_node_name) {
4528 MeterSection* ms = new MeterSection (*child, _frame_rate);
4529 _metrics.push_back (ms);
4532 catch (failed_constructor& err) {
4533 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4534 _metrics = old_metrics;
4535 old_metrics.clear();
4541 if (niter == nlist.end()) {
4542 MetricSectionSorter cmp;
4543 _metrics.sort (cmp);
4546 /* check for legacy sessions where bbt was the base musical unit for tempo */
4547 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4549 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4550 if (t->legacy_bbt().bars != 0) {
4551 fix_legacy_session();
4555 if (t->legacy_end()) {
4556 fix_legacy_end_session();
4564 /* check for multiple tempo/meters at the same location, which
4565 ardour2 somehow allowed.
4568 Metrics::iterator prev = _metrics.end();
4569 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4570 if (prev != _metrics.end()) {
4572 MeterSection* prev_m;
4574 TempoSection* prev_t;
4575 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4576 if (prev_m->pulse() == ms->pulse()) {
4577 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4578 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4581 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4582 if (prev_t->pulse() == ts->pulse()) {
4583 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4584 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4592 recompute_map (_metrics);
4594 Metrics::const_iterator d = old_metrics.begin();
4595 while (d != old_metrics.end()) {
4599 old_metrics.clear ();
4602 PropertyChanged (PropertyChange ());
4608 TempoMap::dump (std::ostream& o) const
4610 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4611 const MeterSection* m;
4612 const TempoSection* t;
4613 const TempoSection* prev_t = 0;
4615 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4617 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4618 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4619 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4620 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4621 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4623 o << " current start : " << t->note_types_per_minute()
4624 << " current end : " << t->end_note_types_per_minute()
4625 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4626 o << " previous : " << prev_t->note_types_per_minute()
4627 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4628 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4629 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4630 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4631 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4634 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4635 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4636 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4637 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4640 o << "------" << std::endl;
4644 TempoMap::n_tempos() const
4646 Glib::Threads::RWLock::ReaderLock lm (lock);
4649 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4650 if ((*i)->is_tempo()) {
4659 TempoMap::n_meters() const
4661 Glib::Threads::RWLock::ReaderLock lm (lock);
4664 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4665 if (!(*i)->is_tempo()) {
4674 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4676 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4677 if ((*i)->frame() >= where && !(*i)->initial ()) {
4681 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4682 gui_set_meter_position (ms, (*i)->frame() + amount);
4685 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4686 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4691 PropertyChanged (PropertyChange ());
4695 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4699 std::list<MetricSection*> metric_kill_list;
4701 TempoSection* last_tempo = NULL;
4702 MeterSection* last_meter = NULL;
4703 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4704 bool meter_after = false; // is there a meter marker likewise?
4706 Glib::Threads::RWLock::WriterLock lm (lock);
4707 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4708 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4709 metric_kill_list.push_back(*i);
4710 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4713 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4717 else if ((*i)->frame() >= where) {
4718 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4719 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4720 if ((*i)->frame() == where) {
4721 // marker was immediately after end of range
4722 tempo_after = dynamic_cast<TempoSection*> (*i);
4723 meter_after = dynamic_cast<MeterSection*> (*i);
4729 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4730 if (last_tempo && !tempo_after) {
4731 metric_kill_list.remove(last_tempo);
4732 last_tempo->set_minute (minute_at_frame (where));
4735 if (last_meter && !meter_after) {
4736 metric_kill_list.remove(last_meter);
4737 last_meter->set_minute (minute_at_frame (where));
4741 //remove all the remaining metrics
4742 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4743 _metrics.remove(*i);
4748 recompute_map (_metrics);
4751 PropertyChanged (PropertyChange ());
4755 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4756 * pos can be -ve, if required.
4759 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4761 Glib::Threads::RWLock::ReaderLock lm (lock);
4762 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4764 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4768 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4770 Glib::Threads::RWLock::ReaderLock lm (lock);
4772 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4773 pos_bbt.ticks += op.ticks;
4774 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4776 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4778 pos_bbt.beats += op.beats;
4779 /* the meter in effect will start on the bar */
4780 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();
4781 while (pos_bbt.beats >= divisions_per_bar + 1) {
4783 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4784 pos_bbt.beats -= divisions_per_bar;
4786 pos_bbt.bars += op.bars;
4788 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4791 /** Count the number of beats that are equivalent to distance when going forward,
4795 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4797 Glib::Threads::RWLock::ReaderLock lm (lock);
4799 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4803 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4809 operator<< (std::ostream& o, const Meter& m) {
4810 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4814 operator<< (std::ostream& o, const Tempo& t) {
4815 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4819 operator<< (std::ostream& o, const MetricSection& section) {
4821 o << "MetricSection @ " << section.frame() << ' ';
4823 const TempoSection* ts;
4824 const MeterSection* ms;
4826 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4827 o << *((const Tempo*) ts);
4828 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4829 o << *((const Meter*) ms);