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);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
55 /* This is tempo- and meter-sensitive. The number it returns
56 is based on the interval between any two lines in the
57 grid that is constructed from tempo and meter sections.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
71 /***********************************************************************/
73 const string TempoSection::xml_state_node_name = "Tempo";
75 TempoSection::TempoSection (const XMLNode& node)
76 : MetricSection (0.0, 0, MusicTime, true)
77 , Tempo (TempoMap::default_tempo())
80 , _locked_to_meter (false)
82 XMLProperty const * prop;
88 _legacy_bbt = BBT_Time (0, 0, 0);
90 if ((prop = node.property ("start")) != 0) {
91 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
95 /* legacy session - start used to be in bbt*/
98 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
104 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
110 if ((prop = node.property ("frame")) != 0) {
111 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
112 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
118 if ((prop = node.property ("beats-per-minute")) == 0) {
119 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
120 throw failed_constructor();
123 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
124 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
125 throw failed_constructor();
128 if ((prop = node.property ("note-type")) == 0) {
129 /* older session, make note type be quarter by default */
132 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
133 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
134 throw failed_constructor();
138 if ((prop = node.property ("movable")) == 0) {
139 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
140 throw failed_constructor();
143 set_movable (string_is_affirmative (prop->value()));
145 if ((prop = node.property ("active")) == 0) {
146 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
149 set_active (string_is_affirmative (prop->value()));
152 if ((prop = node.property ("tempo-type")) == 0) {
155 _type = Type (string_2_enum (prop->value(), _type));
158 if ((prop = node.property ("lock-style")) == 0) {
160 set_position_lock_style (MusicTime);
162 set_position_lock_style (AudioTime);
165 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
168 if ((prop = node.property ("locked-to-meter")) == 0) {
169 set_locked_to_meter (false);
171 set_locked_to_meter (string_is_affirmative (prop->value()));
176 TempoSection::get_state() const
178 XMLNode *root = new XMLNode (xml_state_node_name);
182 snprintf (buf, sizeof (buf), "%lf", pulse());
183 root->add_property ("pulse", buf);
184 snprintf (buf, sizeof (buf), "%li", frame());
185 root->add_property ("frame", buf);
186 snprintf (buf, sizeof (buf), "%lf", _beats_per_minute);
187 root->add_property ("beats-per-minute", buf);
188 snprintf (buf, sizeof (buf), "%lf", _note_type);
189 root->add_property ("note-type", buf);
190 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
191 root->add_property ("movable", buf);
192 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
193 root->add_property ("active", buf);
194 root->add_property ("tempo-type", enum_2_string (_type));
195 root->add_property ("lock-style", enum_2_string (position_lock_style()));
196 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
202 TempoSection::set_type (Type type)
207 /** returns the tempo in beats per minute at the zero-based (relative to session) frame.
210 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
213 if (_type == Constant || _c_func == 0.0) {
214 return beats_per_minute();
217 return _tempo_at_time (frame_to_minute (f - frame(), frame_rate));
220 /** returns the zero-based frame (relative to session)
221 where the tempo in beats per minute occurs in this section.
222 pulse p is only used for constant tempos.
223 note that the tempo map may have multiple such values.
226 TempoSection::frame_at_tempo (const double& bpm, const double& p, const framecnt_t& frame_rate) const
228 if (_type == Constant || _c_func == 0.0) {
229 return ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
232 return minute_to_frame (_time_at_tempo (bpm), frame_rate) + frame();
234 /** returns the tempo in beats per minute at the zero-based (relative to session) pulse.
237 TempoSection::tempo_at_pulse (const double& p) const
240 if (_type == Constant || _c_func == 0.0) {
241 return beats_per_minute();
244 return _tempo_at_pulse (p - pulse());
247 /** returns the zero-based pulse (relative to session)
248 where the tempo in qn beats per minute occurs given frame f. frame f is only used for constant tempi.
249 note that the session tempo map may have multiple beats at a given tempo.
252 TempoSection::pulse_at_tempo (const double& bpm, const framepos_t& f, const framecnt_t& frame_rate) const
254 if (_type == Constant || _c_func == 0.0) {
255 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
258 return _pulse_at_tempo (bpm) + pulse();
261 /** returns the zero-based pulse (relative to session origin)
262 where the zero-based frame (relative to session)
266 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
268 if (_type == Constant || _c_func == 0.0) {
269 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
272 return _pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
275 /** returns the zero-based frame (relative to session start frame)
276 where the zero-based pulse (relative to session start)
281 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
283 if (_type == Constant || _c_func == 0.0) {
284 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
287 return minute_to_frame (_time_at_pulse (p - pulse()), frame_rate) + frame();
295 Tt----|-----------------*|
296 Ta----|--------------|* |
302 _______________|___|____
303 time a t (next tempo)
306 Duration in beats at time a is the integral of some Tempo function.
307 In our case, the Tempo function (Tempo at time t) is
310 with function constant
315 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
316 b(t) = T0(e^(ct) - 1) / c
318 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:
319 t(b) = log((c.b / T0) + 1) / c
321 The time t at which Tempo T occurs is a as above:
322 t(T) = log(T / T0) / c
324 The beat at which a Tempo T occurs is:
327 The Tempo at which beat b occurs is:
330 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
331 Our problem is that we usually don't know t.
332 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.
333 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
334 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
336 By substituting our expanded t as a in the c function above, our problem is reduced to:
337 c = T0 (e^(log (Ta / T0)) - 1) / b
339 Of course the word 'beat' has been left loosely defined above.
340 In music, a beat is defined by the musical pulse (which comes from the tempo)
341 and the meter in use at a particular time (how many pulse divisions there are in one bar).
342 It would be more accurate to substitute the work 'pulse' for 'beat' above.
346 We can now store c for future time calculations.
347 If the following tempo section (the one that defines c in conjunction with this one)
348 is changed or moved, c is no longer valid.
350 The public methods are session-relative.
352 Most of this stuff is taken from this paper:
355 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
358 Zurich University of Arts
359 Institute for Computer Music and Sound Technology
361 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
366 compute this ramp's function constant using the end tempo (in qn beats per minute)
367 and duration (pulses into global start) of some later tempo section.
370 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
372 double const log_tempo_ratio = log (end_bpm / beats_per_minute());
373 return (beats_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
376 /* compute the function constant from some later tempo section, given tempo (quarter notes/min.) and distance (in frames) from session origin */
378 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
380 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
384 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
386 return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
390 TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
392 return (frame / (double) frame_rate) / 60.0;
395 /* position function */
397 TempoSection::a_func (double end_bpm, double c_func) const
399 return log (end_bpm / beats_per_minute()) / c_func;
402 /*function constant*/
404 TempoSection::c_func (double end_bpm, double end_time) const
406 return log (end_bpm / beats_per_minute()) / end_time;
409 /* tempo in bpm at time in minutes */
411 TempoSection::_tempo_at_time (const double& time) const
413 return exp (_c_func * time) * beats_per_minute();
416 /* time in minutes at tempo in bpm */
418 TempoSection::_time_at_tempo (const double& tempo) const
420 return log (tempo / beats_per_minute()) / _c_func;
423 /* pulse at tempo in bpm */
425 TempoSection::_pulse_at_tempo (const double& tempo) const
427 return (tempo - beats_per_minute()) / (_c_func * _note_type);
430 /* tempo in bpm at pulse */
432 TempoSection::_tempo_at_pulse (const double& pulse) const
434 return (pulse * _note_type * _c_func) + beats_per_minute();
437 /* pulse at time in minutes */
439 TempoSection::_pulse_at_time (const double& time) const
441 return expm1 (_c_func * time) * (beats_per_minute() / (_c_func * _note_type));
444 /* time in minutes at pulse */
446 TempoSection::_time_at_pulse (const double& pulse) const
448 return log1p ((_c_func * pulse * _note_type) / beats_per_minute()) / _c_func;
451 /***********************************************************************/
453 const string MeterSection::xml_state_node_name = "Meter";
455 MeterSection::MeterSection (const XMLNode& node)
456 : MetricSection (0.0, 0, MusicTime, false), Meter (TempoMap::default_meter())
458 XMLProperty const * prop;
463 framepos_t frame = 0;
464 pair<double, BBT_Time> start;
466 if ((prop = node.property ("start")) != 0) {
467 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
471 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
473 /* legacy session - start used to be in bbt*/
474 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
479 if ((prop = node.property ("pulse")) != 0) {
480 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
481 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
486 if ((prop = node.property ("beat")) != 0) {
487 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
488 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
494 if ((prop = node.property ("bbt")) == 0) {
495 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
496 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
500 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
501 throw failed_constructor();
507 if ((prop = node.property ("frame")) != 0) {
508 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
509 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
515 /* beats-per-bar is old; divisions-per-bar is new */
517 if ((prop = node.property ("divisions-per-bar")) == 0) {
518 if ((prop = node.property ("beats-per-bar")) == 0) {
519 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
520 throw failed_constructor();
523 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
524 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
525 throw failed_constructor();
528 if ((prop = node.property ("note-type")) == 0) {
529 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
530 throw failed_constructor();
532 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
533 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
534 throw failed_constructor();
537 if ((prop = node.property ("movable")) == 0) {
538 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
539 throw failed_constructor();
542 set_movable (string_is_affirmative (prop->value()));
544 if ((prop = node.property ("lock-style")) == 0) {
545 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
547 set_position_lock_style (MusicTime);
549 set_position_lock_style (AudioTime);
552 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
557 MeterSection::get_state() const
559 XMLNode *root = new XMLNode (xml_state_node_name);
563 snprintf (buf, sizeof (buf), "%lf", pulse());
564 root->add_property ("pulse", buf);
565 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
569 root->add_property ("bbt", buf);
570 snprintf (buf, sizeof (buf), "%lf", beat());
571 root->add_property ("beat", buf);
572 snprintf (buf, sizeof (buf), "%lf", _note_type);
573 root->add_property ("note-type", buf);
574 snprintf (buf, sizeof (buf), "%li", frame());
575 root->add_property ("frame", buf);
576 root->add_property ("lock-style", enum_2_string (position_lock_style()));
577 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
578 root->add_property ("divisions-per-bar", buf);
579 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
580 root->add_property ("movable", buf);
585 /***********************************************************************/
589 The Shaggs - Things I Wonder
590 https://www.youtube.com/watch?v=9wQK6zMJOoQ
592 Tempo is the rate of the musical pulse.
593 Meter divides pulse into measures and beats.
595 TempoSection - provides pulse in the form of beats_per_minute() - the number of quarter notes in one minute.
596 Note that 'beats' in Tempo::beats_per_minute() are quarter notes (pulse based). In the rest of tempo map,
597 'beat' usually refers to accumulated BBT beats (pulse and meter based).
599 MeterSecion - divides pulse into measures (via divisions_per_bar) and beats (via note_divisor).
601 Both tempo and meter have a pulse position and a frame position.
602 Meters also have a beat position, which is always 0.0 for the first one.
603 TempoSection and MeterSection may be locked to either audio or music (position lock style).
604 The lock style determines the 'true' position of the section wich is used to calculate the other postion parameters of the section.
606 The first tempo and first meter are special. they must move together, and must be locked to audio.
607 Audio locked tempos which lie before the first meter are made inactive.
608 They will be re-activated if the first meter is again placed before them.
610 With tempo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
611 referring to the tempo function(s) involved as the distance in whole pulses between a meter and a subsequent beat is
612 sb->beat() - meter->beat() / meter->note_divisor().
613 Because every meter falls on a known pulse, (derived from its bar), the rest is easy as the duration in pulses between
614 two meters is of course
615 (meater_b->bar - meter_a->bar) * meter_a->divisions_per_bar / meter_a->note_divisor.
617 Beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
618 Beat to frame conversion of course requires the use of meter and tempo.
620 Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
621 Here, beats (meters) are used to determine the new pulse (see predict_tempo_position())
623 Recomputing the map is the process where the 'missing' position
624 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
625 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
626 We then use this tempo map (really just the tempos) to find the pulse or frame position of each meter (again depending on lock style).
628 Having done this, we can now find any musical duration by selecting the tempo and meter covering the position (or tempo) in question
629 and querying its appropriate meter/tempo.
631 It is important to keep the _metrics in an order that makes sense.
632 Because ramped MusicTime and AudioTime tempos can interact with each other,
633 reordering is frequent. Care must be taken to keep _metrics in a solved state.
634 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
638 Music and audio-locked objects may seem interchangeable on the surface, but when translating
639 between audio samples and beat, remember that a sample is only a quantised approximation
640 of the actual time (in minutes) of a beat.
641 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
642 mean that this frame is the actual location in time of 1|3|0.
644 You cannot use a frame measurement to determine beat distance except under special circumstances
645 (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).
647 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
648 sample space the user is operating at to be translated correctly to the object.
650 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
651 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
652 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
654 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
656 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
657 The result is rounded to audio frames.
658 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
661 frame_at_beat (beat_at_frame (frame)) == frame
663 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
665 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
666 So instead work in pulses and/or beats and only use beat position to caclulate frame position (e.g. after tempo change).
667 For audio-locked objects, use frame position to calculate beat position.
669 The above pointless example would then do:
670 beat_at_pulse (pulse_at_beat (beat)) to avoid rounding.
673 struct MetricSectionSorter {
674 bool operator() (const MetricSection* a, const MetricSection* b) {
675 return a->pulse() < b->pulse();
679 struct MetricSectionFrameSorter {
680 bool operator() (const MetricSection* a, const MetricSection* b) {
681 return a->frame() < b->frame();
685 TempoMap::TempoMap (framecnt_t fr)
688 BBT_Time start (1, 1, 0);
690 TempoSection *t = new TempoSection (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime);
691 MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
693 t->set_movable (false);
694 m->set_movable (false);
696 /* note: frame time is correct (zero) for both of these */
698 _metrics.push_back (t);
699 _metrics.push_back (m);
703 TempoMap::~TempoMap ()
705 Metrics::const_iterator d = _metrics.begin();
706 while (d != _metrics.end()) {
714 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
716 bool removed = false;
719 Glib::Threads::RWLock::WriterLock lm (lock);
720 if ((removed = remove_tempo_locked (tempo))) {
721 if (complete_operation) {
722 recompute_map (_metrics);
727 if (removed && complete_operation) {
728 PropertyChanged (PropertyChange ());
733 TempoMap::remove_tempo_locked (const TempoSection& tempo)
737 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
738 if (dynamic_cast<TempoSection*> (*i) != 0) {
739 if (tempo.frame() == (*i)->frame()) {
740 if ((*i)->movable()) {
753 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
755 bool removed = false;
758 Glib::Threads::RWLock::WriterLock lm (lock);
759 if ((removed = remove_meter_locked (tempo))) {
760 if (complete_operation) {
761 recompute_map (_metrics);
766 if (removed && complete_operation) {
767 PropertyChanged (PropertyChange ());
772 TempoMap::remove_meter_locked (const MeterSection& meter)
775 if (meter.position_lock_style() == AudioTime) {
776 /* remove meter-locked tempo */
777 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
779 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
780 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
789 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
790 if (dynamic_cast<MeterSection*> (*i) != 0) {
791 if (meter.frame() == (*i)->frame()) {
792 if ((*i)->movable()) {
805 TempoMap::do_insert (MetricSection* section)
807 bool need_add = true;
808 /* we only allow new meters to be inserted on beat 1 of an existing
812 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
814 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
816 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
817 corrected.second.beats = 1;
818 corrected.second.ticks = 0;
819 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
820 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
821 m->bbt(), corrected.second) << endmsg;
822 //m->set_pulse (corrected);
826 /* Look for any existing MetricSection that is of the same type and
827 in the same bar as the new one, and remove it before adding
828 the new one. Note that this means that if we find a matching,
829 existing section, we can break out of the loop since we're
830 guaranteed that there is only one such match.
833 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
835 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
836 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
837 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
838 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
840 if (tempo && insert_tempo) {
843 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
844 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
846 if (!tempo->movable()) {
848 /* can't (re)move this section, so overwrite
849 * its data content (but not its properties as
853 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
854 (*i)->set_position_lock_style (AudioTime);
856 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
857 t->set_type (insert_tempo->type());
867 } else if (meter && insert_meter) {
871 bool const ipm = insert_meter->position_lock_style() == MusicTime;
873 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
875 if (!meter->movable()) {
877 /* can't (re)move this section, so overwrite
878 * its data content (but not its properties as
882 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
883 (*i)->set_position_lock_style (AudioTime);
893 /* non-matching types, so we don't care */
897 /* Add the given MetricSection, if we didn't just reset an existing
902 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
903 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
906 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
907 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
910 bool const ipm = insert_meter->position_lock_style() == MusicTime;
911 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
916 } else if (insert_tempo) {
917 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
918 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
921 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
922 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
929 _metrics.insert (i, section);
930 //dump (_metrics, std::cout);
935 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
937 TempoSection* ts = 0;
939 Glib::Threads::RWLock::WriterLock lm (lock);
940 ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
944 PropertyChanged (PropertyChange ());
950 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
952 const bool locked_to_meter = ts.locked_to_meter();
955 Glib::Threads::RWLock::WriterLock lm (lock);
956 TempoSection& first (first_tempo());
957 if (ts.frame() != first.frame()) {
958 remove_tempo_locked (ts);
959 add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
961 first.set_type (type);
962 first.set_pulse (0.0);
963 first.set_frame (frame);
964 first.set_position_lock_style (AudioTime);
966 /* cannot move the first tempo section */
967 *static_cast<Tempo*>(&first) = tempo;
968 recompute_map (_metrics);
973 PropertyChanged (PropertyChange ());
977 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
978 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
980 TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
981 t->set_locked_to_meter (locked_to_meter);
987 if (pls == AudioTime) {
988 solved = solve_map_frame (_metrics, t, t->frame());
990 solved = solve_map_pulse (_metrics, t, t->pulse());
992 recompute_meters (_metrics);
995 if (!solved && recompute) {
996 recompute_map (_metrics);
1003 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1005 MeterSection* m = 0;
1007 Glib::Threads::RWLock::WriterLock lm (lock);
1008 m = add_meter_locked (meter, beat, where, frame, pls, true);
1013 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1014 dump (_metrics, std::cerr);
1018 PropertyChanged (PropertyChange ());
1023 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1026 Glib::Threads::RWLock::WriterLock lm (lock);
1027 const double beat = beat_at_bbt_locked (_metrics, where);
1030 remove_meter_locked (ms);
1031 add_meter_locked (meter, beat, where, frame, pls, true);
1033 MeterSection& first (first_meter());
1034 TempoSection& first_t (first_tempo());
1035 /* cannot move the first meter section */
1036 *static_cast<Meter*>(&first) = meter;
1037 first.set_position_lock_style (AudioTime);
1038 first.set_pulse (0.0);
1039 first.set_frame (frame);
1040 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1041 first.set_beat (beat);
1042 first_t.set_frame (first.frame());
1043 first_t.set_pulse (0.0);
1044 first_t.set_position_lock_style (AudioTime);
1045 recompute_map (_metrics);
1049 PropertyChanged (PropertyChange ());
1053 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1055 const MeterSection& prev_m = meter_section_at_frame_locked (_metrics, frame - 1);
1056 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1057 TempoSection* mlt = 0;
1059 if (pls == AudioTime) {
1060 /* add meter-locked tempo */
1061 mlt = add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
1069 MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
1070 bool solved = false;
1072 do_insert (new_meter);
1076 if (pls == AudioTime) {
1077 solved = solve_map_frame (_metrics, new_meter, frame);
1079 solved = solve_map_bbt (_metrics, new_meter, where);
1080 /* required due to resetting the pulse of meter-locked tempi above.
1081 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1082 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1084 recompute_map (_metrics);
1088 if (!solved && recompute) {
1089 /* if this has failed to solve, there is little we can do other than to ensure that
1090 the new map is recalculated.
1092 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1093 recompute_map (_metrics);
1100 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1102 Tempo newtempo (beats_per_minute, note_type);
1105 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1106 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1111 Glib::Threads::RWLock::WriterLock lm (lock);
1112 *((Tempo*) t) = newtempo;
1113 recompute_map (_metrics);
1115 PropertyChanged (PropertyChange ());
1122 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1124 Tempo newtempo (beats_per_minute, note_type);
1127 TempoSection* first;
1128 Metrics::iterator i;
1130 /* find the TempoSection immediately preceding "where"
1133 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1135 if ((*i)->frame() > where) {
1141 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1154 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1164 Glib::Threads::RWLock::WriterLock lm (lock);
1165 /* cannot move the first tempo section */
1166 *((Tempo*)prev) = newtempo;
1167 recompute_map (_metrics);
1170 PropertyChanged (PropertyChange ());
1174 TempoMap::first_meter () const
1176 const MeterSection *m = 0;
1178 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1179 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1184 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1185 abort(); /*NOTREACHED*/
1190 TempoMap::first_meter ()
1192 MeterSection *m = 0;
1194 /* CALLER MUST HOLD LOCK */
1196 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1197 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1202 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1203 abort(); /*NOTREACHED*/
1208 TempoMap::first_tempo () const
1210 const TempoSection *t = 0;
1212 /* CALLER MUST HOLD LOCK */
1214 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1215 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1219 if (!t->movable()) {
1225 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1226 abort(); /*NOTREACHED*/
1231 TempoMap::first_tempo ()
1233 TempoSection *t = 0;
1235 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1236 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1240 if (!t->movable()) {
1246 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1247 abort(); /*NOTREACHED*/
1251 TempoMap::recompute_tempi (Metrics& metrics)
1253 TempoSection* prev_t = 0;
1255 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1258 if ((*i)->is_tempo()) {
1259 t = static_cast<TempoSection*> (*i);
1263 if (!t->movable()) {
1271 if (t->position_lock_style() == AudioTime) {
1272 prev_t->set_c_func (prev_t->compute_c_func_frame (t->beats_per_minute(), t->frame(), _frame_rate));
1273 if (!t->locked_to_meter()) {
1274 t->set_pulse (prev_t->pulse_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1278 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->beats_per_minute(), t->pulse(), _frame_rate));
1279 t->set_frame (prev_t->frame_at_tempo (t->beats_per_minute(), t->pulse(), _frame_rate));
1286 prev_t->set_c_func (0.0);
1289 /* tempos must be positioned correctly.
1290 the current approach is to use a meter's bbt time as its base position unit.
1291 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1292 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1295 TempoMap::recompute_meters (Metrics& metrics)
1297 MeterSection* meter = 0;
1298 MeterSection* prev_m = 0;
1300 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1301 if (!(*mi)->is_tempo()) {
1302 meter = static_cast<MeterSection*> (*mi);
1303 if (meter->position_lock_style() == AudioTime) {
1305 pair<double, BBT_Time> b_bbt;
1306 TempoSection* meter_locked_tempo = 0;
1307 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1309 if ((*ii)->is_tempo()) {
1310 t = static_cast<TempoSection*> (*ii);
1311 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1312 meter_locked_tempo = t;
1319 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1320 if (beats + prev_m->beat() != meter->beat()) {
1321 /* reordering caused a bbt change */
1322 b_bbt = make_pair (beats + prev_m->beat()
1323 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1324 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1326 } else if (meter->movable()) {
1327 b_bbt = make_pair (meter->beat(), meter->bbt());
1328 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1331 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1333 if (meter_locked_tempo) {
1334 meter_locked_tempo->set_pulse (pulse);
1336 meter->set_beat (b_bbt);
1337 meter->set_pulse (pulse);
1342 pair<double, BBT_Time> b_bbt;
1344 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1345 if (beats + prev_m->beat() != meter->beat()) {
1346 /* reordering caused a bbt change */
1347 b_bbt = make_pair (beats + prev_m->beat()
1348 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1350 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1352 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1354 /* shouldn't happen - the first is audio-locked */
1355 pulse = pulse_at_beat_locked (metrics, meter->beat());
1356 b_bbt = make_pair (meter->beat(), meter->bbt());
1359 meter->set_beat (b_bbt);
1360 meter->set_pulse (pulse);
1361 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1370 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1372 /* CALLER MUST HOLD WRITE LOCK */
1376 /* we will actually stop once we hit
1383 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1386 /* silly call from Session::process() during startup
1391 recompute_tempi (metrics);
1392 recompute_meters (metrics);
1396 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1398 Glib::Threads::RWLock::ReaderLock lm (lock);
1399 TempoMetric m (first_meter(), first_tempo());
1401 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1402 at something, because we insert the default tempo and meter during
1403 TempoMap construction.
1405 now see if we can find better candidates.
1408 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1410 if ((*i)->frame() > frame) {
1424 /* XX meters only */
1426 TempoMap::metric_at (BBT_Time bbt) const
1428 Glib::Threads::RWLock::ReaderLock lm (lock);
1429 TempoMetric m (first_meter(), first_tempo());
1431 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1432 at something, because we insert the default tempo and meter during
1433 TempoMap construction.
1435 now see if we can find better candidates.
1438 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1440 if (!(*i)->is_tempo()) {
1441 mw = static_cast<MeterSection*> (*i);
1442 BBT_Time section_start (mw->bbt());
1444 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1455 /** Returns the beat duration corresponding to the supplied frame, possibly returning a negative value.
1456 * @param frame The session frame position.
1457 * @return The beat duration according to the tempo map at the supplied frame.
1458 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1459 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1461 * This function uses both tempo and meter.
1464 TempoMap::beat_at_frame (const framecnt_t& frame) const
1466 Glib::Threads::RWLock::ReaderLock lm (lock);
1467 return beat_at_frame_locked (_metrics, frame);
1470 /* This function uses both tempo and meter.*/
1472 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1474 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1475 MeterSection* prev_m = 0;
1476 MeterSection* next_m = 0;
1478 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1479 if (!(*i)->is_tempo()) {
1480 if (prev_m && (*i)->frame() > frame) {
1481 next_m = static_cast<MeterSection*> (*i);
1484 prev_m = static_cast<MeterSection*> (*i);
1488 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1490 /* audio locked meters fake their beat */
1491 if (next_m && next_m->beat() < beat) {
1492 return next_m->beat();
1499 TempoMap::frame_at_beat (const double& beat) const
1501 Glib::Threads::RWLock::ReaderLock lm (lock);
1502 return frame_at_beat_locked (_metrics, beat);
1505 /* meter & tempo section based */
1507 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1509 MeterSection* prev_m = 0;
1510 TempoSection* prev_t = 0;
1514 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1515 if (!(*i)->is_tempo()) {
1516 m = static_cast<MeterSection*> (*i);
1517 if (prev_m && m->beat() > beat) {
1526 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1527 if ((*i)->is_tempo()) {
1528 t = static_cast<TempoSection*> (*i);
1529 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1537 return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1541 TempoMap::tempo_at_frame (const framepos_t& frame) const
1543 Glib::Threads::RWLock::ReaderLock lm (lock);
1544 return tempo_at_frame_locked (_metrics, frame);
1548 TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1550 TempoSection* prev_t = 0;
1554 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1555 if ((*i)->is_tempo()) {
1556 t = static_cast<TempoSection*> (*i);
1560 if ((prev_t) && t->frame() > frame) {
1561 /* t is the section past frame */
1562 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate);
1563 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1570 const double ret = prev_t->beats_per_minute();
1571 const Tempo ret_tempo (ret, prev_t->note_type ());
1576 /** returns the frame at which the supplied tempo occurs, or
1577 * the frame of the last tempo section (search exhausted)
1578 * only the position of the first occurence will be returned
1582 TempoMap::frame_at_tempo (const Tempo& tempo) const
1584 Glib::Threads::RWLock::ReaderLock lm (lock);
1585 return frame_at_tempo_locked (_metrics, tempo);
1590 TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1592 TempoSection* prev_t = 0;
1593 const double tempo_ppm = tempo.beats_per_minute();
1595 Metrics::const_iterator i;
1597 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1599 if ((*i)->is_tempo()) {
1600 t = static_cast<TempoSection*> (*i);
1606 const double t_ppm = t->beats_per_minute();
1608 if (t_ppm == tempo_ppm) {
1613 const double prev_t_ppm = prev_t->beats_per_minute();
1615 if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
1616 return prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
1623 return prev_t->frame();
1626 /** more precise than doing tempo_at_frame (frame_at_beat (b)),
1627 * as there is no intermediate frame rounding.
1630 TempoMap::tempo_at_beat (const double& beat) const
1632 Glib::Threads::RWLock::ReaderLock lm (lock);
1633 const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
1634 const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
1636 return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()), prev_t->note_type());
1640 TempoMap::pulse_at_beat (const double& beat) const
1642 Glib::Threads::RWLock::ReaderLock lm (lock);
1643 return pulse_at_beat_locked (_metrics, beat);
1647 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1649 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1651 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1655 TempoMap::beat_at_pulse (const double& pulse) const
1657 Glib::Threads::RWLock::ReaderLock lm (lock);
1658 return beat_at_pulse_locked (_metrics, pulse);
1662 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1664 MeterSection* prev_m = 0;
1666 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1668 if (!(*i)->is_tempo()) {
1669 m = static_cast<MeterSection*> (*i);
1670 if (prev_m && m->pulse() > pulse) {
1677 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1682 TempoMap::pulse_at_frame (const framepos_t& frame) const
1684 Glib::Threads::RWLock::ReaderLock lm (lock);
1685 return pulse_at_frame_locked (_metrics, frame);
1688 /* tempo section based */
1690 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1692 /* HOLD (at least) THE READER LOCK */
1693 TempoSection* prev_t = 0;
1695 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1697 if ((*i)->is_tempo()) {
1698 t = static_cast<TempoSection*> (*i);
1702 if (prev_t && t->frame() > frame) {
1703 /*the previous ts is the one containing the frame */
1704 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1705 /* audio locked section in new meter*/
1706 if (t->pulse() < ret) {
1715 /* treated as constant for this ts */
1716 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1718 return pulses_in_section + prev_t->pulse();
1722 TempoMap::frame_at_pulse (const double& pulse) const
1724 Glib::Threads::RWLock::ReaderLock lm (lock);
1725 return frame_at_pulse_locked (_metrics, pulse);
1728 /* tempo section based */
1730 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1732 /* HOLD THE READER LOCK */
1734 const TempoSection* prev_t = 0;
1736 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1739 if ((*i)->is_tempo()) {
1740 t = static_cast<TempoSection*> (*i);
1744 if (prev_t && t->pulse() > pulse) {
1745 return prev_t->frame_at_pulse (pulse, _frame_rate);
1751 /* must be treated as constant, irrespective of _type */
1752 double const dtime = (pulse - prev_t->pulse()) * prev_t->frames_per_pulse (_frame_rate);
1754 return (framepos_t) floor (dtime) + prev_t->frame();
1758 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1760 Glib::Threads::RWLock::ReaderLock lm (lock);
1761 return beat_at_bbt_locked (_metrics, bbt);
1766 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1768 /* CALLER HOLDS READ LOCK */
1770 MeterSection* prev_m = 0;
1772 /* because audio-locked meters have 'fake' integral beats,
1773 there is no pulse offset here.
1777 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1778 if (!(*i)->is_tempo()) {
1779 m = static_cast<MeterSection*> (*i);
1781 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1782 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1790 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1791 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1792 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1798 TempoMap::bbt_at_beat (const double& beats)
1800 Glib::Threads::RWLock::ReaderLock lm (lock);
1801 return bbt_at_beat_locked (_metrics, beats);
1805 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1807 /* CALLER HOLDS READ LOCK */
1808 MeterSection* prev_m = 0;
1809 const double beats = max (0.0, b);
1811 MeterSection* m = 0;
1813 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1814 if (!(*i)->is_tempo()) {
1815 m = static_cast<MeterSection*> (*i);
1817 if (m->beat() > beats) {
1818 /* this is the meter after the one our beat is on*/
1827 const double beats_in_ms = beats - prev_m->beat();
1828 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1829 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1830 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1831 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1835 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1836 ret.beats = (uint32_t) floor (remaining_beats);
1837 ret.bars = total_bars;
1839 /* 0 0 0 to 1 1 0 - based mapping*/
1843 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1845 ret.ticks -= BBT_Time::ticks_per_beat;
1848 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1857 TempoMap::pulse_at_bbt (const Timecode::BBT_Time& bbt)
1859 Glib::Threads::RWLock::ReaderLock lm (lock);
1861 return pulse_at_bbt_locked (_metrics, bbt);
1865 TempoMap::pulse_at_bbt_rt (const Timecode::BBT_Time& bbt)
1867 Glib::Threads::RWLock::ReaderLock lm (lock);
1870 throw std::logic_error ("TempoMap::pulse_at_bbt_rt() could not lock tempo map");
1873 return pulse_at_bbt_locked (_metrics, bbt);
1877 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1879 /* CALLER HOLDS READ LOCK */
1881 MeterSection* prev_m = 0;
1883 /* because audio-locked meters have 'fake' integral beats,
1884 there is no pulse offset here.
1888 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1889 if (!(*i)->is_tempo()) {
1890 m = static_cast<MeterSection*> (*i);
1892 if (m->bbt().bars > bbt.bars) {
1900 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1901 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
1902 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
1908 TempoMap::bbt_at_pulse (const double& pulse)
1910 Glib::Threads::RWLock::ReaderLock lm (lock);
1912 return bbt_at_pulse_locked (_metrics, pulse);
1916 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1918 MeterSection* prev_m = 0;
1920 MeterSection* m = 0;
1922 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1924 if (!(*i)->is_tempo()) {
1925 m = static_cast<MeterSection*> (*i);
1928 double const pulses_to_m = m->pulse() - prev_m->pulse();
1929 if (prev_m->pulse() + pulses_to_m > pulse) {
1930 /* this is the meter after the one our beat is on*/
1939 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1940 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1941 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1942 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1943 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1947 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1948 ret.beats = (uint32_t) floor (remaining_beats);
1949 ret.bars = total_bars;
1951 /* 0 0 0 to 1 1 0 mapping*/
1955 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1957 ret.ticks -= BBT_Time::ticks_per_beat;
1960 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1969 TempoMap::bbt_at_frame (framepos_t frame)
1976 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1979 Glib::Threads::RWLock::ReaderLock lm (lock);
1981 return bbt_at_frame_locked (_metrics, frame);
1985 TempoMap::bbt_at_frame_rt (framepos_t frame)
1987 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1990 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
1993 return bbt_at_frame_locked (_metrics, frame);
1997 TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
2007 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
2008 MeterSection* prev_m = 0;
2009 MeterSection* next_m = 0;
2013 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2014 if (!(*i)->is_tempo()) {
2015 m = static_cast<MeterSection*> (*i);
2016 if (prev_m && m->frame() > frame) {
2024 double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
2026 /* handle frame before first meter */
2027 if (frame < prev_m->frame()) {
2030 /* audio locked meters fake their beat */
2031 if (next_m && next_m->beat() < beat) {
2032 beat = next_m->beat();
2035 beat = max (0.0, beat);
2037 const double beats_in_ms = beat - prev_m->beat();
2038 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2039 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2040 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2041 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2045 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2046 ret.beats = (uint32_t) floor (remaining_beats);
2047 ret.bars = total_bars;
2049 /* 0 0 0 to 1 1 0 - based mapping*/
2053 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2055 ret.ticks -= BBT_Time::ticks_per_beat;
2058 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2067 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2070 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2074 if (bbt.beats < 1) {
2075 throw std::logic_error ("beats are counted from one");
2077 Glib::Threads::RWLock::ReaderLock lm (lock);
2079 return frame_at_bbt_locked (_metrics, bbt);
2082 /* meter & tempo section based */
2084 TempoMap::frame_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2086 /* HOLD THE READER LOCK */
2088 const framepos_t ret = frame_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2093 * Returns the distance from 0 in quarter pulses at the supplied frame.
2095 * Plugin APIs don't count ticks in the same way PROGRAM_NAME does.
2096 * We use ticks per beat whereas the rest of the world uses ticks per quarter note.
2097 * This is more or less the VST's ppqPos (a scalar you use to obtain tick position
2098 * in whatever ppqn you're using).
2100 * @param frame The distance in frames relative to session 0 whose quarter note distance you would like.
2101 * @return The quarter note (quarter pulse) distance from session 0 to the supplied frame. Ignores meter.
2105 TempoMap::quarter_note_at_frame (const framepos_t frame)
2107 Glib::Threads::RWLock::ReaderLock lm (lock);
2109 const double ret = quarter_note_at_frame_locked (_metrics, frame);
2115 TempoMap::quarter_note_at_frame_locked (const Metrics& metrics, const framepos_t frame) const
2117 const double ret = pulse_at_frame_locked (metrics, frame) * 4.0;
2123 TempoMap::quarter_note_at_frame_rt (const framepos_t frame)
2125 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2128 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2131 const double ret = pulse_at_frame_locked (_metrics, frame) * 4.0;
2137 TempoMap::frame_at_quarter_note (const double quarter_note)
2139 Glib::Threads::RWLock::ReaderLock lm (lock);
2141 const framepos_t ret = frame_at_quarter_note_locked (_metrics, quarter_note);
2147 TempoMap::frame_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2149 const framepos_t ret = frame_at_pulse_locked (metrics, quarter_note / 4.0);
2155 TempoMap::quarter_note_at_beat (const double beat)
2157 Glib::Threads::RWLock::ReaderLock lm (lock);
2159 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2165 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2167 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2173 TempoMap::beat_at_quarter_note (const double quarter_note)
2175 Glib::Threads::RWLock::ReaderLock lm (lock);
2177 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2182 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2184 const double ret = beat_at_pulse_locked (metrics, quarter_note / 4.0);
2190 TempoMap::check_solved (const Metrics& metrics) const
2192 TempoSection* prev_t = 0;
2193 MeterSection* prev_m = 0;
2195 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2198 if ((*i)->is_tempo()) {
2199 t = static_cast<TempoSection*> (*i);
2204 /* check ordering */
2205 if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
2209 /* precision check ensures tempo and frames align.*/
2210 if (t->frame() != prev_t->frame_at_tempo (t->beats_per_minute(), t->pulse(), _frame_rate)) {
2211 if (!t->locked_to_meter()) {
2216 /* gradient limit - who knows what it should be?
2217 things are also ok (if a little chaotic) without this
2219 if (fabs (prev_t->c_func()) > 1000.0) {
2220 //std::cout << "c : " << prev_t->c_func() << std::endl;
2227 if (!(*i)->is_tempo()) {
2228 m = static_cast<MeterSection*> (*i);
2229 if (prev_m && m->position_lock_style() == AudioTime) {
2230 const TempoSection* t = &tempo_section_at_frame_locked (metrics, m->frame() - 1);
2231 const framepos_t nascent_m_frame = t->frame_at_pulse (m->pulse(), _frame_rate);
2232 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2233 It is complicated by the fact that audio locked meters represent a discontinuity in the pulse
2234 (they place an exact pulse at a particular time expressed only in frames).
2235 This has the effect of shifting the calculated frame at the meter pulse (wrt the previous section of music)
2236 away from its actual frame (which is now the frame location of the exact pulse).
2237 This can result in the calculated frame (from the previous musical section)
2238 differing from the exact frame by one sample.
2241 if (t && (nascent_m_frame > m->frame() + 1 || nascent_m_frame < 0)) {
2255 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2257 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2259 if ((*i)->is_tempo()) {
2260 t = static_cast<TempoSection*> (*i);
2261 if (!t->movable()) {
2262 t->set_active (true);
2265 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2266 t->set_active (false);
2268 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2269 t->set_active (true);
2270 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2279 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
2281 TempoSection* prev_t = 0;
2282 TempoSection* section_prev = 0;
2283 framepos_t first_m_frame = 0;
2285 /* can't move a tempo before the first meter */
2286 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2288 if (!(*i)->is_tempo()) {
2289 m = static_cast<MeterSection*> (*i);
2290 if (!m->movable()) {
2291 first_m_frame = m->frame();
2296 if (section->movable() && frame <= first_m_frame) {
2300 section->set_active (true);
2301 section->set_frame (frame);
2303 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2305 if ((*i)->is_tempo()) {
2306 t = static_cast<TempoSection*> (*i);
2313 section_prev = prev_t;
2314 if (t->locked_to_meter()) {
2319 if (t->position_lock_style() == MusicTime) {
2320 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->beats_per_minute(), t->pulse(), _frame_rate));
2321 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2323 prev_t->set_c_func (prev_t->compute_c_func_frame (t->beats_per_minute(), t->frame(), _frame_rate));
2324 if (!t->locked_to_meter()) {
2325 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2334 section_prev->set_c_func (section_prev->compute_c_func_frame (section->beats_per_minute(), frame, _frame_rate));
2335 if (!section->locked_to_meter()) {
2336 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2341 recompute_tempi (imaginary);
2343 if (check_solved (imaginary)) {
2346 dunp (imaginary, std::cout);
2350 MetricSectionFrameSorter fcmp;
2351 imaginary.sort (fcmp);
2353 recompute_tempi (imaginary);
2355 if (check_solved (imaginary)) {
2363 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2365 TempoSection* prev_t = 0;
2366 TempoSection* section_prev = 0;
2368 section->set_pulse (pulse);
2370 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2372 if ((*i)->is_tempo()) {
2373 t = static_cast<TempoSection*> (*i);
2377 if (!t->movable()) {
2384 section_prev = prev_t;
2387 if (t->position_lock_style() == MusicTime) {
2388 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->beats_per_minute(), t->pulse(), _frame_rate));
2389 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2391 prev_t->set_c_func (prev_t->compute_c_func_frame (t->beats_per_minute(), t->frame(), _frame_rate));
2392 if (!t->locked_to_meter()) {
2393 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2402 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->beats_per_minute(), pulse, _frame_rate));
2403 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2407 recompute_tempi (imaginary);
2409 if (check_solved (imaginary)) {
2412 dunp (imaginary, std::cout);
2416 MetricSectionSorter cmp;
2417 imaginary.sort (cmp);
2419 recompute_tempi (imaginary);
2421 * XX need a restriction here, but only for this case,
2422 * as audio locked tempos don't interact in the same way.
2424 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2426 * |50 bpm |250 bpm |60 bpm
2427 * drag 250 to the pulse after 60->
2428 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2430 if (check_solved (imaginary)) {
2438 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2440 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2441 const MeterSection* other = &meter_section_at_frame_locked (imaginary, frame);
2442 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2446 if (!section->movable()) {
2447 /* lock the first tempo to our first meter */
2448 if (!set_active_tempos (imaginary, frame)) {
2453 TempoSection* meter_locked_tempo = 0;
2455 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2457 if ((*ii)->is_tempo()) {
2458 t = static_cast<TempoSection*> (*ii);
2459 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2460 meter_locked_tempo = t;
2466 if (!meter_locked_tempo) {
2470 MeterSection* prev_m = 0;
2472 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2473 bool solved = false;
2475 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2477 if (!(*i)->is_tempo()) {
2478 m = static_cast<MeterSection*> (*i);
2480 if (prev_m && section->movable()) {
2481 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2482 if (beats + prev_m->beat() < section->beat()) {
2483 /* set the frame/pulse corresponding to its musical position,
2484 * as an earlier time than this has been requested.
2486 const double new_pulse = ((section->beat() - prev_m->beat())
2487 / prev_m->note_divisor()) + prev_m->pulse();
2489 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2491 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2492 meter_locked_tempo->set_pulse (new_pulse);
2493 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2494 section->set_frame (smallest_frame);
2495 section->set_pulse (new_pulse);
2500 Metrics::const_iterator d = future_map.begin();
2501 while (d != future_map.end()) {
2510 /* all is ok. set section's locked tempo if allowed.
2511 possibly disallowed if there is an adjacent audio-locked tempo.
2512 XX this check could possibly go. its never actually happened here.
2514 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_frame_locked (future_map, section->frame()));
2515 meter_copy->set_frame (frame);
2517 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2518 section->set_frame (frame);
2519 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2520 / prev_m->note_divisor()) + prev_m->pulse());
2521 solve_map_frame (imaginary, meter_locked_tempo, frame);
2526 Metrics::const_iterator d = future_map.begin();
2527 while (d != future_map.end()) {
2537 /* not movable (first meter atm) */
2539 tempo_copy->set_frame (frame);
2540 tempo_copy->set_pulse (0.0);
2542 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2543 section->set_frame (frame);
2544 meter_locked_tempo->set_frame (frame);
2545 meter_locked_tempo->set_pulse (0.0);
2546 solve_map_frame (imaginary, meter_locked_tempo, frame);
2551 Metrics::const_iterator d = future_map.begin();
2552 while (d != future_map.end()) {
2561 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2562 section->set_beat (b_bbt);
2563 section->set_pulse (0.0);
2573 MetricSectionFrameSorter fcmp;
2574 imaginary.sort (fcmp);
2576 recompute_meters (imaginary);
2582 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2584 /* disallow setting section to an existing meter's bbt */
2585 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2587 if (!(*i)->is_tempo()) {
2588 m = static_cast<MeterSection*> (*i);
2589 if (m != section && m->bbt().bars == when.bars) {
2595 MeterSection* prev_m = 0;
2596 MeterSection* section_prev = 0;
2598 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2600 if (!(*i)->is_tempo()) {
2601 m = static_cast<MeterSection*> (*i);
2602 pair<double, BBT_Time> b_bbt;
2603 double new_pulse = 0.0;
2605 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2606 section_prev = prev_m;
2607 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2608 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2609 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2611 section->set_beat (b_bbt);
2612 section->set_pulse (pulse);
2613 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2618 if (m->position_lock_style() == AudioTime) {
2619 TempoSection* meter_locked_tempo = 0;
2621 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2623 if ((*ii)->is_tempo()) {
2624 t = static_cast<TempoSection*> (*ii);
2625 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2626 meter_locked_tempo = t;
2632 if (!meter_locked_tempo) {
2637 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2639 if (beats + prev_m->beat() != m->beat()) {
2640 /* tempo/ meter change caused a change in beat (bar). */
2641 b_bbt = make_pair (beats + prev_m->beat()
2642 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2643 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2644 } else if (m->movable()) {
2645 b_bbt = make_pair (m->beat(), m->bbt());
2646 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2649 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2652 meter_locked_tempo->set_pulse (new_pulse);
2653 m->set_beat (b_bbt);
2654 m->set_pulse (new_pulse);
2658 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2659 if (beats + prev_m->beat() != m->beat()) {
2660 /* tempo/ meter change caused a change in beat (bar). */
2661 b_bbt = make_pair (beats + prev_m->beat()
2662 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2664 b_bbt = make_pair (beats + prev_m->beat()
2667 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2668 m->set_beat (b_bbt);
2669 m->set_pulse (new_pulse);
2670 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2677 if (!section_prev) {
2679 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2680 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2681 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2683 section->set_beat (b_bbt);
2684 section->set_pulse (pulse);
2685 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2688 MetricSectionSorter cmp;
2689 imaginary.sort (cmp);
2691 recompute_meters (imaginary);
2696 /** places a copy of _metrics into copy and returns a pointer
2697 * to section's equivalent in copy.
2700 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2702 TempoSection* ret = 0;
2704 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2707 if ((*i)->is_tempo()) {
2708 t = static_cast<TempoSection*> (*i);
2710 ret = new TempoSection (*t);
2711 copy.push_back (ret);
2715 TempoSection* cp = new TempoSection (*t);
2716 copy.push_back (cp);
2718 if (!(*i)->is_tempo()) {
2719 m = static_cast<MeterSection *> (*i);
2720 MeterSection* cp = new MeterSection (*m);
2721 copy.push_back (cp);
2729 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2731 MeterSection* ret = 0;
2733 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2736 if ((*i)->is_tempo()) {
2737 t = static_cast<TempoSection*> (*i);
2738 TempoSection* cp = new TempoSection (*t);
2739 copy.push_back (cp);
2742 if (!(*i)->is_tempo()) {
2743 m = static_cast<MeterSection *> (*i);
2745 ret = new MeterSection (*m);
2746 copy.push_back (ret);
2749 MeterSection* cp = new MeterSection (*m);
2750 copy.push_back (cp);
2757 /** answers the question "is this a valid beat position for this tempo section?".
2758 * it returns true if the tempo section can be moved to the requested bbt position,
2759 * leaving the tempo map in a solved state.
2760 * @param section the tempo section to be moved
2761 * @param bbt the requested new position for the tempo section
2762 * @return true if the tempo section can be moved to the position, otherwise false.
2765 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2768 TempoSection* tempo_copy = 0;
2771 Glib::Threads::RWLock::ReaderLock lm (lock);
2772 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2778 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2780 Metrics::const_iterator d = copy.begin();
2781 while (d != copy.end()) {
2790 * 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,
2791 * taking any possible reordering as a consequence of this into account.
2792 * @param section - the section to be altered
2793 * @param bbt - the bbt where the altered tempo will fall
2794 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
2796 pair<double, framepos_t>
2797 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
2800 pair<double, framepos_t> ret = make_pair (0.0, 0);
2802 Glib::Threads::RWLock::ReaderLock lm (lock);
2804 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2806 const double beat = beat_at_bbt_locked (future_map, bbt);
2808 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2809 ret.first = tempo_copy->pulse();
2810 ret.second = tempo_copy->frame();
2812 ret.first = section->pulse();
2813 ret.second = section->frame();
2816 Metrics::const_iterator d = future_map.begin();
2817 while (d != future_map.end()) {
2825 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
2829 if (ts->position_lock_style() == MusicTime) {
2831 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
2832 Glib::Threads::RWLock::WriterLock lm (lock);
2833 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2835 tempo_copy->set_position_lock_style (AudioTime);
2837 if (solve_map_frame (future_map, tempo_copy, frame)) {
2838 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
2839 const double pulse = pulse_at_beat_locked (future_map, beat);
2841 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
2842 solve_map_pulse (_metrics, ts, pulse);
2843 recompute_meters (_metrics);
2851 Glib::Threads::RWLock::WriterLock lm (lock);
2852 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2854 if (solve_map_frame (future_map, tempo_copy, frame)) {
2856 /* We're moving the object that defines the grid while snapping to it...
2857 * Placing the ts at the beat corresponding to the requested frame may shift the
2858 * grid in such a way that the mouse is left hovering over a completerly different division,
2859 * causing jittering when the mouse next moves (esp. large tempo deltas).
2860 * To avoid this, place the ts at the requested frame in a dummy map
2861 * then find the closest beat subdivision to that frame in the dummy.
2862 * This alters the snap behaviour slightly in that we snap to beat divisions
2863 * in the future map rather than the existing one.
2865 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
2866 const double pulse = pulse_at_beat_locked (future_map, beat);
2868 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
2869 /* snapping to a grid. force MusicTime temporarily. */
2870 ts->set_position_lock_style (MusicTime);
2871 solve_map_pulse (_metrics, ts, pulse);
2872 ts->set_position_lock_style (AudioTime);
2874 recompute_meters (_metrics);
2877 solve_map_frame (_metrics, ts, frame);
2878 recompute_meters (_metrics);
2884 Metrics::const_iterator d = future_map.begin();
2885 while (d != future_map.end()) {
2890 MetricPositionChanged (); // Emit Signal
2894 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2898 if (ms->position_lock_style() == AudioTime) {
2901 Glib::Threads::RWLock::WriterLock lm (lock);
2902 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2904 if (solve_map_frame (future_map, copy, frame)) {
2905 solve_map_frame (_metrics, ms, frame);
2906 recompute_tempi (_metrics);
2911 Glib::Threads::RWLock::WriterLock lm (lock);
2912 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2914 const double beat = beat_at_frame_locked (_metrics, frame);
2915 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
2917 if (solve_map_bbt (future_map, copy, bbt)) {
2918 solve_map_bbt (_metrics, ms, bbt);
2919 recompute_tempi (_metrics);
2924 Metrics::const_iterator d = future_map.begin();
2925 while (d != future_map.end()) {
2930 MetricPositionChanged (); // Emit Signal
2934 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2937 bool can_solve = false;
2939 Glib::Threads::RWLock::WriterLock lm (lock);
2940 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2941 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2942 recompute_tempi (future_map);
2944 if (check_solved (future_map)) {
2945 ts->set_beats_per_minute (bpm.beats_per_minute());
2946 recompute_map (_metrics);
2951 Metrics::const_iterator d = future_map.begin();
2952 while (d != future_map.end()) {
2957 MetricPositionChanged (); // Emit Signal
2963 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2966 Ts (future prev_t) Tnext
2969 |----------|----------
2976 Glib::Threads::RWLock::WriterLock lm (lock);
2982 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2983 TempoSection* prev_to_prev_t = 0;
2984 const frameoffset_t fr_off = end_frame - frame;
2986 if (prev_t && prev_t->pulse() > 0.0) {
2987 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (future_map, prev_t->frame() - 1));
2990 TempoSection* next_t = 0;
2991 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2992 TempoSection* t = 0;
2993 if ((*i)->is_tempo()) {
2994 t = static_cast<TempoSection*> (*i);
2995 if (t->frame() > ts->frame()) {
3001 /* minimum allowed measurement distance in frames */
3002 const framepos_t min_dframe = 2;
3004 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3005 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3007 double contribution = 0.0;
3009 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3010 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3013 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3015 const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
3016 const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
3020 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3022 if (prev_t->position_lock_style() == MusicTime) {
3023 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3024 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3026 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
3027 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3029 new_bpm = prev_t->beats_per_minute();
3032 /* prev to prev is irrelevant */
3034 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3035 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3037 new_bpm = prev_t->beats_per_minute();
3042 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3043 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3045 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
3046 / (double) ((end_frame) - prev_to_prev_t->frame()));
3048 new_bpm = prev_t->beats_per_minute();
3051 /* prev_to_prev_t is irrelevant */
3053 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3054 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3056 new_bpm = prev_t->beats_per_minute();
3062 double frame_ratio = 1.0;
3063 double pulse_ratio = 1.0;
3064 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
3066 if (prev_to_prev_t) {
3067 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3068 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3070 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3071 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3074 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3075 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3077 pulse_ratio = (start_pulse / end_pulse);
3079 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
3082 /* don't clamp and proceed here.
3083 testing has revealed that this can go negative,
3084 which is an entirely different thing to just being too low.
3086 if (new_bpm < 0.5) {
3089 new_bpm = min (new_bpm, (double) 1000.0);
3090 prev_t->set_beats_per_minute (new_bpm);
3091 recompute_tempi (future_map);
3092 recompute_meters (future_map);
3094 if (check_solved (future_map)) {
3095 ts->set_beats_per_minute (new_bpm);
3096 recompute_tempi (_metrics);
3097 recompute_meters (_metrics);
3101 Metrics::const_iterator d = future_map.begin();
3102 while (d != future_map.end()) {
3107 MetricPositionChanged (); // Emit Signal
3110 /** Returns the exact beat corresponding to the bar, beat or quarter note subdivision nearest to
3111 * the supplied frame, possibly returning a negative value.
3112 * @param frame The session frame position.
3113 * @param sub_num The subdivision to use when rounding the beat.
3114 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3115 * Positive integers indicate quarter note (non BBT) divisions.
3116 * 0 indicates that the returned beat should not be rounded.
3117 * @return The beat position of the supplied frame.
3118 * If the supplied frame lies before the first meter, the return will be negative,
3119 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3120 * the continuation of the tempo curve (backwards).
3122 * This function uses both tempo and meter.
3125 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num)
3127 Glib::Threads::RWLock::ReaderLock lm (lock);
3129 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3133 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions)
3135 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3138 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3139 * the supplied frame, possibly returning a negative value.
3140 * @param frame The session frame position.
3141 * @param sub_num The subdivision to use when rounding the quarter note.
3142 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3143 * Positive integers indicate quarter note (non BBT) divisions.
3144 * 0 indicates that the returned quarter note should not be rounded.
3145 * @return The quarter note position of the supplied frame.
3146 * If the supplied frame lies before the first meter, the return will be negative,
3147 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3148 * the continuation of the tempo curve (backwards).
3150 * This function uses both tempo and meter.
3153 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num)
3155 Glib::Threads::RWLock::ReaderLock lm (lock);
3157 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3161 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num)
3163 double qn = quarter_note_at_frame_locked (metrics, frame);
3166 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3167 } else if (sub_num == 1) {
3168 /* the gui requested exact musical (BBT) beat */
3169 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_frame_locked (metrics, frame) + 0.5));
3170 } else if (sub_num == -1) {
3172 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3176 const double prev_b = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
3178 const double next_b = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
3180 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3191 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3193 Glib::Threads::RWLock::ReaderLock lm (lock);
3195 BBT_Time pos_bbt = bbt_at_frame_locked (_metrics, pos);
3196 const framecnt_t offset = frame_at_bbt_locked (_metrics, pos_bbt);
3197 const double divisions = meter_section_at_frame_locked (_metrics, pos).divisions_per_bar();
3200 pos_bbt.bars += bbt.bars;
3202 pos_bbt.ticks += bbt.ticks;
3203 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3205 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3208 pos_bbt.beats += bbt.beats;
3209 if ((double) pos_bbt.beats > divisions) {
3211 pos_bbt.beats -= divisions;
3214 return frame_at_bbt_locked (_metrics, pos_bbt) - offset;
3216 pos_bbt.bars -= bbt.bars;
3218 if (pos_bbt.ticks < bbt.ticks) {
3219 if (pos_bbt.beats == 1) {
3221 pos_bbt.beats = divisions;
3225 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3227 pos_bbt.ticks -= bbt.ticks;
3230 if (pos_bbt.beats <= bbt.beats) {
3232 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3234 pos_bbt.beats -= bbt.beats;
3237 return offset - frame_at_bbt_locked (_metrics, pos_bbt);
3244 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3246 return round_to_type (fr, dir, Bar);
3250 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3252 return round_to_type (fr, dir, Beat);
3256 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3258 Glib::Threads::RWLock::ReaderLock lm (lock);
3259 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_frame_locked (_metrics, fr)) * BBT_Time::ticks_per_beat);
3260 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3261 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3263 ticks -= beats * BBT_Time::ticks_per_beat;
3266 /* round to next (or same iff dir == RoundUpMaybe) */
3268 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3270 if (mod == 0 && dir == RoundUpMaybe) {
3271 /* right on the subdivision, which is fine, so do nothing */
3273 } else if (mod == 0) {
3274 /* right on the subdivision, so the difference is just the subdivision ticks */
3275 ticks += ticks_one_subdivisions_worth;
3278 /* not on subdivision, compute distance to next subdivision */
3280 ticks += ticks_one_subdivisions_worth - mod;
3283 if (ticks >= BBT_Time::ticks_per_beat) {
3284 ticks -= BBT_Time::ticks_per_beat;
3286 } else if (dir < 0) {
3288 /* round to previous (or same iff dir == RoundDownMaybe) */
3290 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3292 if (difference == 0 && dir == RoundDownAlways) {
3293 /* right on the subdivision, but force-rounding down,
3294 so the difference is just the subdivision ticks */
3295 difference = ticks_one_subdivisions_worth;
3298 if (ticks < difference) {
3299 ticks = BBT_Time::ticks_per_beat - ticks;
3301 ticks -= difference;
3305 /* round to nearest */
3308 /* compute the distance to the previous and next subdivision */
3310 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3312 /* closer to the next subdivision, so shift forward */
3314 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3316 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3318 if (ticks > BBT_Time::ticks_per_beat) {
3320 ticks -= BBT_Time::ticks_per_beat;
3321 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3324 } else if (rem > 0) {
3326 /* closer to previous subdivision, so shift backward */
3330 /* can't go backwards past zero, so ... */
3333 /* step back to previous beat */
3335 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3336 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3338 ticks = lrint (ticks - rem);
3339 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3342 /* on the subdivision, do nothing */
3346 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
3352 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3354 Glib::Threads::RWLock::ReaderLock lm (lock);
3355 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_frame_locked (_metrics, fr)) * BBT_Time::ticks_per_beat);
3356 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3357 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3359 ticks -= beats * BBT_Time::ticks_per_beat;
3362 /* round to next (or same iff dir == RoundUpMaybe) */
3364 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3366 if (mod == 0 && dir == RoundUpMaybe) {
3367 /* right on the subdivision, which is fine, so do nothing */
3369 } else if (mod == 0) {
3370 /* right on the subdivision, so the difference is just the subdivision ticks */
3371 ticks += ticks_one_subdivisions_worth;
3374 /* not on subdivision, compute distance to next subdivision */
3376 ticks += ticks_one_subdivisions_worth - mod;
3379 if (ticks >= BBT_Time::ticks_per_beat) {
3380 ticks -= BBT_Time::ticks_per_beat;
3382 } else if (dir < 0) {
3384 /* round to previous (or same iff dir == RoundDownMaybe) */
3386 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3388 if (difference == 0 && dir == RoundDownAlways) {
3389 /* right on the subdivision, but force-rounding down,
3390 so the difference is just the subdivision ticks */
3391 difference = ticks_one_subdivisions_worth;
3394 if (ticks < difference) {
3395 ticks = BBT_Time::ticks_per_beat - ticks;
3397 ticks -= difference;
3401 /* round to nearest */
3404 /* compute the distance to the previous and next subdivision */
3406 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3408 /* closer to the next subdivision, so shift forward */
3410 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3412 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3414 if (ticks > BBT_Time::ticks_per_beat) {
3416 ticks -= BBT_Time::ticks_per_beat;
3417 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3420 } else if (rem > 0) {
3422 /* closer to previous subdivision, so shift backward */
3426 /* can't go backwards past zero, so ... */
3429 /* step back to previous beat */
3431 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3432 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3434 ticks = lrint (ticks - rem);
3435 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3438 /* on the subdivision, do nothing */
3442 const framepos_t ret_frame = frame_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
3448 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3450 Glib::Threads::RWLock::ReaderLock lm (lock);
3452 const double beat_at_framepos = max (0.0, beat_at_frame_locked (_metrics, frame));
3453 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3458 /* find bar previous to 'frame' */
3461 return frame_at_bbt_locked (_metrics, bbt);
3463 } else if (dir > 0) {
3464 /* find bar following 'frame' */
3468 return frame_at_bbt_locked (_metrics, bbt);
3470 /* true rounding: find nearest bar */
3471 framepos_t raw_ft = frame_at_bbt_locked (_metrics, bbt);
3474 framepos_t prev_ft = frame_at_bbt_locked (_metrics, bbt);
3476 framepos_t next_ft = frame_at_bbt_locked (_metrics, bbt);
3478 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3489 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3490 } else if (dir > 0) {
3491 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3493 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3502 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3503 framepos_t lower, framepos_t upper)
3505 Glib::Threads::RWLock::ReaderLock lm (lock);
3506 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3508 /* although the map handles negative beats, bbt doesn't. */
3513 if (frame_at_beat_locked (_metrics, cnt) >= upper) {
3517 while (pos >= 0 && pos < upper) {
3518 pos = frame_at_beat_locked (_metrics, cnt);
3519 const TempoSection tempo = tempo_section_at_frame_locked (_metrics, pos);
3520 const MeterSection meter = meter_section_at_frame_locked (_metrics, pos);
3521 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3522 points.push_back (BBTPoint (meter, tempo_at_frame_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3528 TempoMap::tempo_section_at_frame (framepos_t frame) const
3530 Glib::Threads::RWLock::ReaderLock lm (lock);
3531 return tempo_section_at_frame_locked (_metrics, frame);
3535 TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3537 TempoSection* prev = 0;
3541 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3543 if ((*i)->is_tempo()) {
3544 t = static_cast<TempoSection*> (*i);
3548 if (prev && t->frame() > frame) {
3558 abort(); /*NOTREACHED*/
3565 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3567 TempoSection* prev_t = 0;
3568 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3572 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3573 if ((*i)->is_tempo()) {
3574 t = static_cast<TempoSection*> (*i);
3575 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3585 /* don't use this to calculate length (the tempo is only correct for this frame).
3586 do that stuff based on the beat_at_frame and frame_at_beat api
3589 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3591 Glib::Threads::RWLock::ReaderLock lm (lock);
3593 const TempoSection* ts_at = 0;
3594 const TempoSection* ts_after = 0;
3595 Metrics::const_iterator i;
3598 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3600 if ((*i)->is_tempo()) {
3601 t = static_cast<TempoSection*> (*i);
3605 if (ts_at && (*i)->frame() > frame) {
3614 return (60.0 * _frame_rate) / ts_at->tempo_at_frame (frame, _frame_rate);
3616 /* must be treated as constant tempo */
3617 return ts_at->frames_per_beat (_frame_rate);
3621 TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3623 Metrics::const_iterator i;
3624 MeterSection* prev = 0;
3628 for (i = metrics.begin(); i != metrics.end(); ++i) {
3630 if (!(*i)->is_tempo()) {
3631 m = static_cast<MeterSection*> (*i);
3633 if (prev && (*i)->frame() > frame) {
3643 abort(); /*NOTREACHED*/
3651 TempoMap::meter_section_at_frame (framepos_t frame) const
3653 Glib::Threads::RWLock::ReaderLock lm (lock);
3654 return meter_section_at_frame_locked (_metrics, frame);
3658 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3660 MeterSection* prev_m = 0;
3662 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3664 if (!(*i)->is_tempo()) {
3665 m = static_cast<MeterSection*> (*i);
3666 if (prev_m && m->beat() > beat) {
3677 TempoMap::meter_section_at_beat (double beat) const
3679 Glib::Threads::RWLock::ReaderLock lm (lock);
3680 return meter_section_at_beat_locked (_metrics, beat);
3684 TempoMap::meter_at_frame (framepos_t frame) const
3686 TempoMetric m (metric_at (frame));
3691 TempoMap::fix_legacy_session ()
3693 MeterSection* prev_m = 0;
3694 TempoSection* prev_t = 0;
3696 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3700 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3701 if (!m->movable()) {
3702 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3706 m->set_position_lock_style (AudioTime);
3711 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3712 + (m->bbt().beats - 1)
3713 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3715 m->set_beat (start);
3716 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3717 + (m->bbt().beats - 1)
3718 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3719 m->set_pulse (start_beat / prev_m->note_divisor());
3722 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3728 if (!t->movable()) {
3731 t->set_position_lock_style (AudioTime);
3737 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3738 + (t->legacy_bbt().beats - 1)
3739 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3741 t->set_pulse (beat / prev_m->note_divisor());
3743 /* really shouldn't happen but.. */
3744 t->set_pulse (beat / 4.0);
3753 TempoMap::get_state ()
3755 Metrics::const_iterator i;
3756 XMLNode *root = new XMLNode ("TempoMap");
3759 Glib::Threads::RWLock::ReaderLock lm (lock);
3760 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3761 root->add_child_nocopy ((*i)->get_state());
3769 TempoMap::set_state (const XMLNode& node, int /*version*/)
3772 Glib::Threads::RWLock::WriterLock lm (lock);
3775 XMLNodeConstIterator niter;
3776 Metrics old_metrics (_metrics);
3779 nlist = node.children();
3781 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3782 XMLNode* child = *niter;
3784 if (child->name() == TempoSection::xml_state_node_name) {
3787 TempoSection* ts = new TempoSection (*child);
3788 _metrics.push_back (ts);
3791 catch (failed_constructor& err){
3792 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3793 _metrics = old_metrics;
3794 old_metrics.clear();
3798 } else if (child->name() == MeterSection::xml_state_node_name) {
3801 MeterSection* ms = new MeterSection (*child);
3802 _metrics.push_back (ms);
3805 catch (failed_constructor& err) {
3806 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3807 _metrics = old_metrics;
3808 old_metrics.clear();
3814 if (niter == nlist.end()) {
3815 MetricSectionSorter cmp;
3816 _metrics.sort (cmp);
3819 /* check for legacy sessions where bbt was the base musical unit for tempo */
3820 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3822 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3823 if (t->legacy_bbt().bars != 0) {
3824 fix_legacy_session();
3831 /* check for multiple tempo/meters at the same location, which
3832 ardour2 somehow allowed.
3835 Metrics::iterator prev = _metrics.end();
3836 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3837 if (prev != _metrics.end()) {
3839 MeterSection* prev_m;
3841 TempoSection* prev_t;
3842 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3843 if (prev_m->pulse() == ms->pulse()) {
3844 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3845 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3848 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3849 if (prev_t->pulse() == ts->pulse()) {
3850 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3851 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3859 recompute_map (_metrics);
3861 Metrics::const_iterator d = old_metrics.begin();
3862 while (d != old_metrics.end()) {
3866 old_metrics.clear ();
3869 PropertyChanged (PropertyChange ());
3875 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3877 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3878 const MeterSection* m;
3879 const TempoSection* t;
3880 const TempoSection* prev_t = 0;
3882 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3884 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3885 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3886 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3887 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3889 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3890 o << "calculated : " << prev_t->tempo_at_pulse (t->pulse()) << " | " << prev_t->pulse_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate) << " | " << prev_t->frame_at_tempo (t->beats_per_minute(), t->pulse(), _frame_rate) << std::endl;
3893 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3894 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3895 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3898 o << "------" << std::endl;
3902 TempoMap::n_tempos() const
3904 Glib::Threads::RWLock::ReaderLock lm (lock);
3907 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3908 if ((*i)->is_tempo()) {
3917 TempoMap::n_meters() const
3919 Glib::Threads::RWLock::ReaderLock lm (lock);
3922 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3923 if (!(*i)->is_tempo()) {
3932 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3934 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
3935 if ((*i)->frame() >= where && (*i)->movable ()) {
3939 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
3940 gui_move_meter (ms, (*i)->frame() + amount);
3943 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
3944 gui_move_tempo (ts, (*i)->frame() + amount, 0);
3949 PropertyChanged (PropertyChange ());
3953 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3957 std::list<MetricSection*> metric_kill_list;
3959 TempoSection* last_tempo = NULL;
3960 MeterSection* last_meter = NULL;
3961 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3962 bool meter_after = false; // is there a meter marker likewise?
3964 Glib::Threads::RWLock::WriterLock lm (lock);
3965 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3966 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3967 metric_kill_list.push_back(*i);
3968 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3971 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3975 else if ((*i)->frame() >= where) {
3976 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3977 (*i)->set_frame ((*i)->frame() - amount);
3978 if ((*i)->frame() == where) {
3979 // marker was immediately after end of range
3980 tempo_after = dynamic_cast<TempoSection*> (*i);
3981 meter_after = dynamic_cast<MeterSection*> (*i);
3987 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3988 if (last_tempo && !tempo_after) {
3989 metric_kill_list.remove(last_tempo);
3990 last_tempo->set_frame(where);
3993 if (last_meter && !meter_after) {
3994 metric_kill_list.remove(last_meter);
3995 last_meter->set_frame(where);
3999 //remove all the remaining metrics
4000 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4001 _metrics.remove(*i);
4006 recompute_map (_metrics);
4009 PropertyChanged (PropertyChange ());
4013 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4014 * pos can be -ve, if required.
4017 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats quarter_note) const
4019 Glib::Threads::RWLock::ReaderLock lm (lock);
4021 return frame_at_quarter_note_locked (_metrics, quarter_note_at_frame_locked (_metrics, frame) + quarter_note.to_double());
4025 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4027 Glib::Threads::RWLock::ReaderLock lm (lock);
4029 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos));
4030 pos_bbt.ticks += op.ticks;
4031 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4033 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4035 pos_bbt.beats += op.beats;
4036 /* the meter in effect will start on the bar */
4037 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();
4038 while (pos_bbt.beats >= divisions_per_bar + 1) {
4040 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4041 pos_bbt.beats -= divisions_per_bar;
4043 pos_bbt.bars += op.bars;
4045 return frame_at_bbt_locked (_metrics, pos_bbt);
4048 /** Count the number of beats that are equivalent to distance when going forward,
4052 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4054 Glib::Threads::RWLock::ReaderLock lm (lock);
4056 return Evoral::Beats (quarter_note_at_frame_locked (_metrics, pos + distance) - quarter_note_at_frame_locked (_metrics, pos));
4060 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4066 operator<< (std::ostream& o, const Meter& m) {
4067 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4071 operator<< (std::ostream& o, const Tempo& t) {
4072 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
4076 operator<< (std::ostream& o, const MetricSection& section) {
4078 o << "MetricSection @ " << section.frame() << ' ';
4080 const TempoSection* ts;
4081 const MeterSection* ms;
4083 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4084 o << *((const Tempo*) ts);
4085 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4086 o << *((const Meter*) ms);