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 whole pulses 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 pulses_per_minute();
217 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
220 /** returns the zero-based frame (relative to session)
221 where the tempo in whole pulses 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& ppm, 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_pulse_tempo (ppm), frame_rate) + frame();
234 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
237 TempoSection::tempo_at_pulse (const double& p) const
240 if (_type == Constant || _c_func == 0.0) {
241 return pulses_per_minute();
243 double const ppm = pulse_tempo_at_pulse (p - pulse());
247 /** returns the zero-based beat (relative to session)
248 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
249 note that the session tempo map may have multiple beats at a given tempo.
252 TempoSection::pulse_at_tempo (const double& ppm, 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_pulse_tempo (ppm) + 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 whole pulses 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 / pulses_per_minute());
373 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
376 /* compute the function constant from some later tempo section, given tempo (whole pulses/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_ppm, double c_func) const
399 return log (end_ppm / pulses_per_minute()) / c_func;
402 /*function constant*/
404 TempoSection::c_func (double end_ppm, double end_time) const
406 return log (end_ppm / pulses_per_minute()) / end_time;
409 /* tempo in ppm at time in minutes */
411 TempoSection::pulse_tempo_at_time (const double& time) const
413 return exp (_c_func * time) * pulses_per_minute();
416 /* time in minutes at tempo in ppm */
418 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
420 return log (pulse_tempo / pulses_per_minute()) / _c_func;
423 /* tick at tempo in ppm */
425 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
427 return (pulse_tempo - pulses_per_minute()) / _c_func;
430 /* tempo in ppm at tick */
432 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
434 return (pulse * _c_func) + pulses_per_minute();
437 /* pulse at time in minutes */
439 TempoSection::pulse_at_time (const double& time) const
441 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
444 /* time in minutes at pulse */
446 TempoSection::time_at_pulse (const double& pulse) const
448 return log1p ((_c_func * pulse) / pulses_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 Meters divide the pulses into measures and beats.
595 TempoSections - provide pulses in the form of beats_per_minute() and note_type() where note_type is the division of a whole pulse,
596 and beats_per_minute is the number of note_types in one minute (unlike what its name suggests).
597 Note that Tempo::beats_per_minute() has nothing to do with musical beats. It has been left that way because
598 a shorter one hasn't been found yet (pulse_divisions_per_minute()?).
600 MeterSecions - divide pulses into measures (via divisions_per_bar) and beats (via note_divisor).
602 Both tempos and meters have a pulse position and a frame position.
603 Meters also have a beat position, which is always 0.0 for the first meter.
604 TempoSections and MeterSections may be locked to either audio or music (position lock style).
605 The lock style determines the 'true' position of the section wich is used to calculate the other postion parameters of the section.
607 The first tempo and first meter are special. they must move together, and must be locked to audio.
608 Audio locked tempos which lie before the first meter are made inactive.
609 They will be re-activated if the first meter is again placed before them.
611 With tepo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
612 referring to the tempo function(s) involved as the distance in whole pulses between a meter and a subsequent beat is
613 sb->beat() - meter->beat() / meter->note_divisor().
614 Because every meter falls on a known pulse, (derived from its bar), the rest is easy as the duration in pulses between
615 two meters is of course
616 (meater_b->bar - meter_a->bar) * meter_a->divisions_per_bar / meter_a->note_divisor.
618 Below, beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
619 Beat to frame conversion of course requires the use of meter and tempo.
621 Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
622 Here, beats (meters) are used to determine the new pulse (see predict_tempo_position())
624 Recomputing the map is the process where the 'missing' position
625 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
626 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
627 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).
629 Having done this, we can now find any musical duration by selecting the tempo and meter covering the position (or tempo) in question
630 and querying its appropriate meter/tempo.
632 It is important to keep the _metrics in an order that makes sense.
633 Because ramped MusicTime and AudioTime tempos can interact with each other,
634 reordering is frequent. Care must be taken to keep _metrics in a solved state.
635 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
639 Music and audio-locked objects may seem interchangeable on the surface, but when translating
640 between audio samples and beats, keep in mind that a sample is only a quantised approximation
641 of the actual time (in minutes) of a beat.
642 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
643 mean that this frame is the actual location in time of 1|3|0.
645 You cannot use a frame measurement to determine beat distance except under special circumstances
646 (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).
648 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
649 sample space the user is operating at to be translated correctly to the object.
651 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
652 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
653 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
655 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
657 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
658 The result is rounded to audio frames.
659 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
662 frame_at_beat (beat_at_frame (frame)) == frame
664 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
666 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
667 So instead work in pulses and/or beats and only use beat position to caclulate frame position (e.g. after tempo change).
668 For audio-locked objects, use frame position to calculate beat position.
670 The above pointless example would then do:
671 beat_at_pulse (pulse_at_beat (beat)) to avoid rounding.
674 struct MetricSectionSorter {
675 bool operator() (const MetricSection* a, const MetricSection* b) {
676 return a->pulse() < b->pulse();
680 struct MetricSectionFrameSorter {
681 bool operator() (const MetricSection* a, const MetricSection* b) {
682 return a->frame() < b->frame();
686 TempoMap::TempoMap (framecnt_t fr)
689 BBT_Time start (1, 1, 0);
691 TempoSection *t = new TempoSection (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime);
692 MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
694 t->set_movable (false);
695 m->set_movable (false);
697 /* note: frame time is correct (zero) for both of these */
699 _metrics.push_back (t);
700 _metrics.push_back (m);
704 TempoMap::~TempoMap ()
706 Metrics::const_iterator d = _metrics.begin();
707 while (d != _metrics.end()) {
715 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
717 bool removed = false;
720 Glib::Threads::RWLock::WriterLock lm (lock);
721 if ((removed = remove_tempo_locked (tempo))) {
722 if (complete_operation) {
723 recompute_map (_metrics);
728 if (removed && complete_operation) {
729 PropertyChanged (PropertyChange ());
734 TempoMap::remove_tempo_locked (const TempoSection& tempo)
738 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
739 if (dynamic_cast<TempoSection*> (*i) != 0) {
740 if (tempo.frame() == (*i)->frame()) {
741 if ((*i)->movable()) {
754 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
756 bool removed = false;
759 Glib::Threads::RWLock::WriterLock lm (lock);
760 if ((removed = remove_meter_locked (tempo))) {
761 if (complete_operation) {
762 recompute_map (_metrics);
767 if (removed && complete_operation) {
768 PropertyChanged (PropertyChange ());
773 TempoMap::remove_meter_locked (const MeterSection& meter)
776 if (meter.position_lock_style() == AudioTime) {
777 /* remove meter-locked tempo */
778 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
780 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
781 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
790 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
791 if (dynamic_cast<MeterSection*> (*i) != 0) {
792 if (meter.frame() == (*i)->frame()) {
793 if ((*i)->movable()) {
806 TempoMap::do_insert (MetricSection* section)
808 bool need_add = true;
809 /* we only allow new meters to be inserted on beat 1 of an existing
813 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
815 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
817 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
818 corrected.second.beats = 1;
819 corrected.second.ticks = 0;
820 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
821 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
822 m->bbt(), corrected.second) << endmsg;
823 //m->set_pulse (corrected);
827 /* Look for any existing MetricSection that is of the same type and
828 in the same bar as the new one, and remove it before adding
829 the new one. Note that this means that if we find a matching,
830 existing section, we can break out of the loop since we're
831 guaranteed that there is only one such match.
834 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
836 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
837 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
838 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
839 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
841 if (tempo && insert_tempo) {
844 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
845 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
847 if (!tempo->movable()) {
849 /* can't (re)move this section, so overwrite
850 * its data content (but not its properties as
854 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
855 (*i)->set_position_lock_style (AudioTime);
857 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
858 t->set_type (insert_tempo->type());
868 } else if (meter && insert_meter) {
872 bool const ipm = insert_meter->position_lock_style() == MusicTime;
874 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
876 if (!meter->movable()) {
878 /* can't (re)move this section, so overwrite
879 * its data content (but not its properties as
883 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
884 (*i)->set_position_lock_style (AudioTime);
894 /* non-matching types, so we don't care */
898 /* Add the given MetricSection, if we didn't just reset an existing
903 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
904 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
907 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
908 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
911 bool const ipm = insert_meter->position_lock_style() == MusicTime;
912 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
917 } else if (insert_tempo) {
918 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
919 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
922 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
923 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
930 _metrics.insert (i, section);
931 //dump (_metrics, std::cout);
936 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
938 TempoSection* ts = 0;
940 Glib::Threads::RWLock::WriterLock lm (lock);
941 ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
945 PropertyChanged (PropertyChange ());
951 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
953 const bool locked_to_meter = ts.locked_to_meter();
956 Glib::Threads::RWLock::WriterLock lm (lock);
957 TempoSection& first (first_tempo());
958 if (ts.frame() != first.frame()) {
959 remove_tempo_locked (ts);
960 add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
962 first.set_type (type);
963 first.set_pulse (0.0);
964 first.set_frame (frame);
965 first.set_position_lock_style (AudioTime);
967 /* cannot move the first tempo section */
968 *static_cast<Tempo*>(&first) = tempo;
969 recompute_map (_metrics);
974 PropertyChanged (PropertyChange ());
978 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
979 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
981 TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
982 t->set_locked_to_meter (locked_to_meter);
988 if (pls == AudioTime) {
989 solved = solve_map_frame (_metrics, t, t->frame());
991 solved = solve_map_pulse (_metrics, t, t->pulse());
993 recompute_meters (_metrics);
996 if (!solved && recompute) {
997 recompute_map (_metrics);
1004 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1006 MeterSection* m = 0;
1008 Glib::Threads::RWLock::WriterLock lm (lock);
1009 m = add_meter_locked (meter, beat, where, frame, pls, true);
1014 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1015 dump (_metrics, std::cerr);
1019 PropertyChanged (PropertyChange ());
1024 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1027 Glib::Threads::RWLock::WriterLock lm (lock);
1028 const double beat = beat_at_bbt_locked (_metrics, where);
1031 remove_meter_locked (ms);
1032 add_meter_locked (meter, beat, where, frame, pls, true);
1034 MeterSection& first (first_meter());
1035 TempoSection& first_t (first_tempo());
1036 /* cannot move the first meter section */
1037 *static_cast<Meter*>(&first) = meter;
1038 first.set_position_lock_style (AudioTime);
1039 first.set_pulse (0.0);
1040 first.set_frame (frame);
1041 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1042 first.set_beat (beat);
1043 first_t.set_frame (first.frame());
1044 first_t.set_pulse (0.0);
1045 first_t.set_position_lock_style (AudioTime);
1046 recompute_map (_metrics);
1050 PropertyChanged (PropertyChange ());
1054 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1056 const MeterSection& prev_m = meter_section_at_frame_locked (_metrics, frame - 1);
1057 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1058 TempoSection* mlt = 0;
1060 if (pls == AudioTime) {
1061 /* add meter-locked tempo */
1062 mlt = add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
1070 MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
1071 bool solved = false;
1073 do_insert (new_meter);
1077 if (pls == AudioTime) {
1078 solved = solve_map_frame (_metrics, new_meter, frame);
1080 solved = solve_map_bbt (_metrics, new_meter, where);
1081 /* required due to resetting the pulse of meter-locked tempi above.
1082 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1083 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1085 recompute_map (_metrics);
1089 if (!solved && recompute) {
1090 /* if this has failed to solve, there is little we can do other than to ensure that
1091 the new map is recalculated.
1093 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1094 recompute_map (_metrics);
1101 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1103 Tempo newtempo (beats_per_minute, note_type);
1106 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1107 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1112 Glib::Threads::RWLock::WriterLock lm (lock);
1113 *((Tempo*) t) = newtempo;
1114 recompute_map (_metrics);
1116 PropertyChanged (PropertyChange ());
1123 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1125 Tempo newtempo (beats_per_minute, note_type);
1128 TempoSection* first;
1129 Metrics::iterator i;
1131 /* find the TempoSection immediately preceding "where"
1134 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1136 if ((*i)->frame() > where) {
1142 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1155 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1165 Glib::Threads::RWLock::WriterLock lm (lock);
1166 /* cannot move the first tempo section */
1167 *((Tempo*)prev) = newtempo;
1168 recompute_map (_metrics);
1171 PropertyChanged (PropertyChange ());
1175 TempoMap::first_meter () const
1177 const MeterSection *m = 0;
1179 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1180 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1185 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1186 abort(); /*NOTREACHED*/
1191 TempoMap::first_meter ()
1193 MeterSection *m = 0;
1195 /* CALLER MUST HOLD LOCK */
1197 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1198 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1203 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1204 abort(); /*NOTREACHED*/
1209 TempoMap::first_tempo () const
1211 const TempoSection *t = 0;
1213 /* CALLER MUST HOLD LOCK */
1215 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1216 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1220 if (!t->movable()) {
1226 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1227 abort(); /*NOTREACHED*/
1232 TempoMap::first_tempo ()
1234 TempoSection *t = 0;
1236 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1237 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1241 if (!t->movable()) {
1247 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1248 abort(); /*NOTREACHED*/
1252 TempoMap::recompute_tempi (Metrics& metrics)
1254 TempoSection* prev_t = 0;
1256 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1259 if ((*i)->is_tempo()) {
1260 t = static_cast<TempoSection*> (*i);
1264 if (!t->movable()) {
1272 if (t->position_lock_style() == AudioTime) {
1273 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1274 if (!t->locked_to_meter()) {
1275 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1279 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1280 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1287 prev_t->set_c_func (0.0);
1290 /* tempos must be positioned correctly.
1291 the current approach is to use a meter's bbt time as its base position unit.
1292 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1293 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1296 TempoMap::recompute_meters (Metrics& metrics)
1298 MeterSection* meter = 0;
1299 MeterSection* prev_m = 0;
1301 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1302 if (!(*mi)->is_tempo()) {
1303 meter = static_cast<MeterSection*> (*mi);
1304 if (meter->position_lock_style() == AudioTime) {
1306 pair<double, BBT_Time> b_bbt;
1307 TempoSection* meter_locked_tempo = 0;
1308 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1310 if ((*ii)->is_tempo()) {
1311 t = static_cast<TempoSection*> (*ii);
1312 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1313 meter_locked_tempo = t;
1320 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1321 if (beats + prev_m->beat() != meter->beat()) {
1322 /* reordering caused a bbt change */
1323 b_bbt = make_pair (beats + prev_m->beat()
1324 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1325 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1327 } else if (meter->movable()) {
1328 b_bbt = make_pair (meter->beat(), meter->bbt());
1329 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1332 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1334 if (meter_locked_tempo) {
1335 meter_locked_tempo->set_pulse (pulse);
1337 meter->set_beat (b_bbt);
1338 meter->set_pulse (pulse);
1343 pair<double, BBT_Time> b_bbt;
1345 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1346 if (beats + prev_m->beat() != meter->beat()) {
1347 /* reordering caused a bbt change */
1348 b_bbt = make_pair (beats + prev_m->beat()
1349 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1351 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1353 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1355 /* shouldn't happen - the first is audio-locked */
1356 pulse = pulse_at_beat_locked (metrics, meter->beat());
1357 b_bbt = make_pair (meter->beat(), meter->bbt());
1360 meter->set_beat (b_bbt);
1361 meter->set_pulse (pulse);
1362 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1371 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1373 /* CALLER MUST HOLD WRITE LOCK */
1377 /* we will actually stop once we hit
1384 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1387 /* silly call from Session::process() during startup
1392 recompute_tempi (metrics);
1393 recompute_meters (metrics);
1397 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1399 Glib::Threads::RWLock::ReaderLock lm (lock);
1400 TempoMetric m (first_meter(), first_tempo());
1402 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1403 at something, because we insert the default tempo and meter during
1404 TempoMap construction.
1406 now see if we can find better candidates.
1409 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1411 if ((*i)->frame() > frame) {
1425 /* XX meters only */
1427 TempoMap::metric_at (BBT_Time bbt) const
1429 Glib::Threads::RWLock::ReaderLock lm (lock);
1430 TempoMetric m (first_meter(), first_tempo());
1432 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1433 at something, because we insert the default tempo and meter during
1434 TempoMap construction.
1436 now see if we can find better candidates.
1439 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1441 if (!(*i)->is_tempo()) {
1442 mw = static_cast<MeterSection*> (*i);
1443 BBT_Time section_start (mw->bbt());
1445 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1456 /** Returns the beat duration corresponding to the supplied frame, possibly returning a negative value.
1457 * @param frame The session frame position.
1458 * @return The beat duration according to the tempo map at the supplied frame.
1459 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1460 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1462 * This function uses both tempo and meter.
1465 TempoMap::beat_at_frame (const framecnt_t& frame) const
1467 Glib::Threads::RWLock::ReaderLock lm (lock);
1468 return beat_at_frame_locked (_metrics, frame);
1471 /* This function uses both tempo and meter.*/
1473 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1475 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1476 MeterSection* prev_m = 0;
1477 MeterSection* next_m = 0;
1479 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1480 if (!(*i)->is_tempo()) {
1481 if (prev_m && (*i)->frame() > frame) {
1482 next_m = static_cast<MeterSection*> (*i);
1485 prev_m = static_cast<MeterSection*> (*i);
1489 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1491 /* audio locked meters fake their beat */
1492 if (next_m && next_m->beat() < beat) {
1493 return next_m->beat();
1500 TempoMap::frame_at_beat (const double& beat) const
1502 Glib::Threads::RWLock::ReaderLock lm (lock);
1503 return frame_at_beat_locked (_metrics, beat);
1506 /* meter & tempo section based */
1508 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1510 MeterSection* prev_m = 0;
1511 TempoSection* prev_t = 0;
1515 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1516 if (!(*i)->is_tempo()) {
1517 m = static_cast<MeterSection*> (*i);
1518 if (prev_m && m->beat() > beat) {
1527 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1528 if ((*i)->is_tempo()) {
1529 t = static_cast<TempoSection*> (*i);
1530 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1538 return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1542 TempoMap::tempo_at_frame (const framepos_t& frame) const
1544 Glib::Threads::RWLock::ReaderLock lm (lock);
1545 return tempo_at_frame_locked (_metrics, frame);
1549 TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1551 TempoSection* prev_t = 0;
1555 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1556 if ((*i)->is_tempo()) {
1557 t = static_cast<TempoSection*> (*i);
1561 if ((prev_t) && t->frame() > frame) {
1562 /* t is the section past frame */
1563 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
1564 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1571 const double ret = prev_t->beats_per_minute();
1572 const Tempo ret_tempo (ret, prev_t->note_type ());
1577 /** returns the frame at which the supplied tempo occurs, or
1578 * the frame of the last tempo section (search exhausted)
1579 * only the position of the first occurence will be returned
1583 TempoMap::frame_at_tempo (const Tempo& tempo) const
1585 Glib::Threads::RWLock::ReaderLock lm (lock);
1586 return frame_at_tempo_locked (_metrics, tempo);
1591 TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1593 TempoSection* prev_t = 0;
1594 const double tempo_ppm = tempo.beats_per_minute() / tempo.note_type();
1596 Metrics::const_iterator i;
1598 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1600 if ((*i)->is_tempo()) {
1601 t = static_cast<TempoSection*> (*i);
1607 const double t_ppm = t->beats_per_minute() / t->note_type();
1609 if (t_ppm == tempo_ppm) {
1614 const double prev_t_ppm = prev_t->beats_per_minute() / prev_t->note_type();
1616 if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
1617 return prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
1624 return prev_t->frame();
1627 /** more precise than doing tempo_at_frame (frame_at_beat (b)),
1628 * as there is no intermediate frame rounding.
1631 TempoMap::tempo_at_beat (const double& beat) const
1633 Glib::Threads::RWLock::ReaderLock lm (lock);
1634 const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
1635 const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
1636 const double note_type = prev_t->note_type();
1638 return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()) * note_type, note_type);
1642 TempoMap::pulse_at_beat (const double& beat) const
1644 Glib::Threads::RWLock::ReaderLock lm (lock);
1645 return pulse_at_beat_locked (_metrics, beat);
1649 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1651 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1653 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1657 TempoMap::beat_at_pulse (const double& pulse) const
1659 Glib::Threads::RWLock::ReaderLock lm (lock);
1660 return beat_at_pulse_locked (_metrics, pulse);
1664 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1666 MeterSection* prev_m = 0;
1668 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1670 if (!(*i)->is_tempo()) {
1671 m = static_cast<MeterSection*> (*i);
1672 if (prev_m && m->pulse() > pulse) {
1673 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1681 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1686 TempoMap::pulse_at_frame (const framepos_t& frame) const
1688 Glib::Threads::RWLock::ReaderLock lm (lock);
1689 return pulse_at_frame_locked (_metrics, frame);
1692 /* tempo section based */
1694 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1696 /* HOLD (at least) THE READER LOCK */
1697 TempoSection* prev_t = 0;
1699 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1701 if ((*i)->is_tempo()) {
1702 t = static_cast<TempoSection*> (*i);
1706 if (prev_t && t->frame() > frame) {
1707 /*the previous ts is the one containing the frame */
1708 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
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 (framecnt_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, Glib::Threads::TRY_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)
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, Glib::Threads::TRY_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)
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, Glib::Threads::TRY_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)
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, Glib::Threads::TRY_LOCK);
2177 const double ret = beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2183 TempoMap::check_solved (const Metrics& metrics) const
2185 TempoSection* prev_t = 0;
2186 MeterSection* prev_m = 0;
2188 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2191 if ((*i)->is_tempo()) {
2192 t = static_cast<TempoSection*> (*i);
2197 /* check ordering */
2198 if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
2202 /* precision check ensures tempo and frames align.*/
2203 if (t->frame() != prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
2204 if (!t->locked_to_meter()) {
2209 /* gradient limit - who knows what it should be?
2210 things are also ok (if a little chaotic) without this
2212 if (fabs (prev_t->c_func()) > 1000.0) {
2213 //std::cout << "c : " << prev_t->c_func() << std::endl;
2220 if (!(*i)->is_tempo()) {
2221 m = static_cast<MeterSection*> (*i);
2222 if (prev_m && m->position_lock_style() == AudioTime) {
2223 const TempoSection* t = &tempo_section_at_frame_locked (metrics, m->frame() - 1);
2224 const framepos_t nascent_m_frame = t->frame_at_pulse (m->pulse(), _frame_rate);
2225 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2226 It is complicated by the fact that audio locked meters represent a discontinuity in the pulse
2227 (they place an exact pulse at a particular time expressed only in frames).
2228 This has the effect of shifting the calculated frame at the meter pulse (wrt the previous section of music)
2229 away from its actual frame (which is now the frame location of the exact pulse).
2230 This can result in the calculated frame (from the previous musical section)
2231 differing from the exact frame by one sample.
2234 if (t && (nascent_m_frame > m->frame() + 1 || nascent_m_frame < 0)) {
2248 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2250 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2252 if ((*i)->is_tempo()) {
2253 t = static_cast<TempoSection*> (*i);
2254 if (!t->movable()) {
2255 t->set_active (true);
2258 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2259 t->set_active (false);
2261 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2262 t->set_active (true);
2263 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2272 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
2274 TempoSection* prev_t = 0;
2275 TempoSection* section_prev = 0;
2276 framepos_t first_m_frame = 0;
2278 /* can't move a tempo before the first meter */
2279 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2281 if (!(*i)->is_tempo()) {
2282 m = static_cast<MeterSection*> (*i);
2283 if (!m->movable()) {
2284 first_m_frame = m->frame();
2289 if (section->movable() && frame <= first_m_frame) {
2293 section->set_active (true);
2294 section->set_frame (frame);
2296 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2298 if ((*i)->is_tempo()) {
2299 t = static_cast<TempoSection*> (*i);
2306 section_prev = prev_t;
2307 if (t->locked_to_meter()) {
2312 if (t->position_lock_style() == MusicTime) {
2313 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2314 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2316 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2317 if (!t->locked_to_meter()) {
2318 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2327 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
2328 if (!section->locked_to_meter()) {
2329 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2334 recompute_tempi (imaginary);
2336 if (check_solved (imaginary)) {
2339 dunp (imaginary, std::cout);
2343 MetricSectionFrameSorter fcmp;
2344 imaginary.sort (fcmp);
2346 recompute_tempi (imaginary);
2348 if (check_solved (imaginary)) {
2356 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2358 TempoSection* prev_t = 0;
2359 TempoSection* section_prev = 0;
2361 section->set_pulse (pulse);
2363 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2365 if ((*i)->is_tempo()) {
2366 t = static_cast<TempoSection*> (*i);
2370 if (!t->movable()) {
2377 section_prev = prev_t;
2380 if (t->position_lock_style() == MusicTime) {
2381 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2382 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2384 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2385 if (!t->locked_to_meter()) {
2386 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2395 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2396 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2400 recompute_tempi (imaginary);
2402 if (check_solved (imaginary)) {
2405 dunp (imaginary, std::cout);
2409 MetricSectionSorter cmp;
2410 imaginary.sort (cmp);
2412 recompute_tempi (imaginary);
2414 * XX need a restriction here, but only for this case,
2415 * as audio locked tempos don't interact in the same way.
2417 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2419 * |50 bpm |250 bpm |60 bpm
2420 * drag 250 to the pulse after 60->
2421 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2423 if (check_solved (imaginary)) {
2431 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2433 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2434 const MeterSection* other = &meter_section_at_frame_locked (imaginary, frame);
2435 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2439 if (!section->movable()) {
2440 /* lock the first tempo to our first meter */
2441 if (!set_active_tempos (imaginary, frame)) {
2446 TempoSection* meter_locked_tempo = 0;
2448 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2450 if ((*ii)->is_tempo()) {
2451 t = static_cast<TempoSection*> (*ii);
2452 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2453 meter_locked_tempo = t;
2459 if (!meter_locked_tempo) {
2463 MeterSection* prev_m = 0;
2465 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2466 bool solved = false;
2468 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2470 if (!(*i)->is_tempo()) {
2471 m = static_cast<MeterSection*> (*i);
2473 if (prev_m && section->movable()) {
2474 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2475 if (beats + prev_m->beat() < section->beat()) {
2476 /* set the frame/pulse corresponding to its musical position,
2477 * as an earlier time than this has been requested.
2479 const double new_pulse = ((section->beat() - prev_m->beat())
2480 / prev_m->note_divisor()) + prev_m->pulse();
2482 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2484 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2485 meter_locked_tempo->set_pulse (new_pulse);
2486 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2487 section->set_frame (smallest_frame);
2488 section->set_pulse (new_pulse);
2493 Metrics::const_iterator d = future_map.begin();
2494 while (d != future_map.end()) {
2503 /* all is ok. set section's locked tempo if allowed.
2504 possibly disallowed if there is an adjacent audio-locked tempo.
2505 XX this check could possibly go. its never actually happened here.
2507 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_frame_locked (future_map, section->frame()));
2508 meter_copy->set_frame (frame);
2510 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2511 section->set_frame (frame);
2512 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2513 / prev_m->note_divisor()) + prev_m->pulse());
2514 solve_map_frame (imaginary, meter_locked_tempo, frame);
2519 Metrics::const_iterator d = future_map.begin();
2520 while (d != future_map.end()) {
2530 /* not movable (first meter atm) */
2532 tempo_copy->set_frame (frame);
2533 tempo_copy->set_pulse (0.0);
2535 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2536 section->set_frame (frame);
2537 meter_locked_tempo->set_frame (frame);
2538 meter_locked_tempo->set_pulse (0.0);
2539 solve_map_frame (imaginary, meter_locked_tempo, frame);
2544 Metrics::const_iterator d = future_map.begin();
2545 while (d != future_map.end()) {
2554 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2555 section->set_beat (b_bbt);
2556 section->set_pulse (0.0);
2566 MetricSectionFrameSorter fcmp;
2567 imaginary.sort (fcmp);
2569 recompute_meters (imaginary);
2575 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2577 /* disallow setting section to an existing meter's bbt */
2578 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2580 if (!(*i)->is_tempo()) {
2581 m = static_cast<MeterSection*> (*i);
2582 if (m != section && m->bbt().bars == when.bars) {
2588 MeterSection* prev_m = 0;
2589 MeterSection* section_prev = 0;
2591 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2593 if (!(*i)->is_tempo()) {
2594 m = static_cast<MeterSection*> (*i);
2595 pair<double, BBT_Time> b_bbt;
2596 double new_pulse = 0.0;
2598 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2599 section_prev = prev_m;
2600 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2601 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2602 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2604 section->set_beat (b_bbt);
2605 section->set_pulse (pulse);
2606 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2611 if (m->position_lock_style() == AudioTime) {
2612 TempoSection* meter_locked_tempo = 0;
2614 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2616 if ((*ii)->is_tempo()) {
2617 t = static_cast<TempoSection*> (*ii);
2618 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2619 meter_locked_tempo = t;
2625 if (!meter_locked_tempo) {
2630 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2632 if (beats + prev_m->beat() != m->beat()) {
2633 /* tempo/ meter change caused a change in beat (bar). */
2634 b_bbt = make_pair (beats + prev_m->beat()
2635 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2636 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2637 } else if (m->movable()) {
2638 b_bbt = make_pair (m->beat(), m->bbt());
2639 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2642 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2645 meter_locked_tempo->set_pulse (new_pulse);
2646 m->set_beat (b_bbt);
2647 m->set_pulse (new_pulse);
2651 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2652 if (beats + prev_m->beat() != m->beat()) {
2653 /* tempo/ meter change caused a change in beat (bar). */
2654 b_bbt = make_pair (beats + prev_m->beat()
2655 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2657 b_bbt = make_pair (beats + prev_m->beat()
2660 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2661 m->set_beat (b_bbt);
2662 m->set_pulse (new_pulse);
2663 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2670 if (!section_prev) {
2672 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2673 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2674 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2676 section->set_beat (b_bbt);
2677 section->set_pulse (pulse);
2678 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2681 MetricSectionSorter cmp;
2682 imaginary.sort (cmp);
2684 recompute_meters (imaginary);
2689 /** places a copy of _metrics into copy and returns a pointer
2690 * to section's equivalent in copy.
2693 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2695 TempoSection* ret = 0;
2697 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2700 if ((*i)->is_tempo()) {
2701 t = static_cast<TempoSection*> (*i);
2703 ret = new TempoSection (*t);
2704 copy.push_back (ret);
2708 TempoSection* cp = new TempoSection (*t);
2709 copy.push_back (cp);
2711 if (!(*i)->is_tempo()) {
2712 m = static_cast<MeterSection *> (*i);
2713 MeterSection* cp = new MeterSection (*m);
2714 copy.push_back (cp);
2722 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2724 MeterSection* ret = 0;
2726 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2729 if ((*i)->is_tempo()) {
2730 t = static_cast<TempoSection*> (*i);
2731 TempoSection* cp = new TempoSection (*t);
2732 copy.push_back (cp);
2735 if (!(*i)->is_tempo()) {
2736 m = static_cast<MeterSection *> (*i);
2738 ret = new MeterSection (*m);
2739 copy.push_back (ret);
2742 MeterSection* cp = new MeterSection (*m);
2743 copy.push_back (cp);
2750 /** answers the question "is this a valid beat position for this tempo section?".
2751 * it returns true if the tempo section can be moved to the requested bbt position,
2752 * leaving the tempo map in a solved state.
2753 * @param section the tempo section to be moved
2754 * @param bbt the requested new position for the tempo section
2755 * @return true if the tempo section can be moved to the position, otherwise false.
2758 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2761 TempoSection* tempo_copy = 0;
2764 Glib::Threads::RWLock::ReaderLock lm (lock);
2765 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2771 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2773 Metrics::const_iterator d = copy.begin();
2774 while (d != copy.end()) {
2783 * 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,
2784 * taking any possible reordering as a consequence of this into account.
2785 * @param section - the section to be altered
2786 * @param bbt - the bbt where the altered tempo will fall
2787 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
2789 pair<double, framepos_t>
2790 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
2793 pair<double, framepos_t> ret = make_pair (0.0, 0);
2795 Glib::Threads::RWLock::ReaderLock lm (lock);
2797 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2799 const double beat = beat_at_bbt_locked (future_map, bbt);
2801 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2802 ret.first = tempo_copy->pulse();
2803 ret.second = tempo_copy->frame();
2805 ret.first = section->pulse();
2806 ret.second = section->frame();
2809 Metrics::const_iterator d = future_map.begin();
2810 while (d != future_map.end()) {
2818 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
2822 if (ts->position_lock_style() == MusicTime) {
2824 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
2825 Glib::Threads::RWLock::WriterLock lm (lock);
2826 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2828 tempo_copy->set_position_lock_style (AudioTime);
2830 if (solve_map_frame (future_map, tempo_copy, frame)) {
2831 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
2832 const double pulse = pulse_at_beat_locked (future_map, beat);
2834 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
2835 solve_map_pulse (_metrics, ts, pulse);
2836 recompute_meters (_metrics);
2844 Glib::Threads::RWLock::WriterLock lm (lock);
2845 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2847 if (solve_map_frame (future_map, tempo_copy, frame)) {
2849 /* We're moving the object that defines the grid while snapping to it...
2850 * Placing the ts at the beat corresponding to the requested frame may shift the
2851 * grid in such a way that the mouse is left hovering over a completerly different division,
2852 * causing jittering when the mouse next moves (esp. large tempo deltas).
2853 * To avoid this, place the ts at the requested frame in a dummy map
2854 * then find the closest beat subdivision to that frame in the dummy.
2855 * This alters the snap behaviour slightly in that we snap to beat divisions
2856 * in the future map rather than the existing one.
2858 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
2859 const double pulse = pulse_at_beat_locked (future_map, beat);
2861 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
2862 /* snapping to a grid. force MusicTime temporarily. */
2863 ts->set_position_lock_style (MusicTime);
2864 solve_map_pulse (_metrics, ts, pulse);
2865 ts->set_position_lock_style (AudioTime);
2867 recompute_meters (_metrics);
2870 solve_map_frame (_metrics, ts, frame);
2871 recompute_meters (_metrics);
2877 Metrics::const_iterator d = future_map.begin();
2878 while (d != future_map.end()) {
2883 MetricPositionChanged (); // Emit Signal
2887 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2891 if (ms->position_lock_style() == AudioTime) {
2894 Glib::Threads::RWLock::WriterLock lm (lock);
2895 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2897 if (solve_map_frame (future_map, copy, frame)) {
2898 solve_map_frame (_metrics, ms, frame);
2899 recompute_tempi (_metrics);
2904 Glib::Threads::RWLock::WriterLock lm (lock);
2905 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2907 const double beat = beat_at_frame_locked (_metrics, frame);
2908 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
2910 if (solve_map_bbt (future_map, copy, bbt)) {
2911 solve_map_bbt (_metrics, ms, bbt);
2912 recompute_tempi (_metrics);
2917 Metrics::const_iterator d = future_map.begin();
2918 while (d != future_map.end()) {
2923 MetricPositionChanged (); // Emit Signal
2927 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2930 bool can_solve = false;
2932 Glib::Threads::RWLock::WriterLock lm (lock);
2933 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2934 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2935 recompute_tempi (future_map);
2937 if (check_solved (future_map)) {
2938 ts->set_beats_per_minute (bpm.beats_per_minute());
2939 recompute_map (_metrics);
2944 Metrics::const_iterator d = future_map.begin();
2945 while (d != future_map.end()) {
2950 MetricPositionChanged (); // Emit Signal
2956 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2959 Ts (future prev_t) Tnext
2962 |----------|----------
2969 Glib::Threads::RWLock::WriterLock lm (lock);
2975 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2976 TempoSection* prev_to_prev_t = 0;
2977 const frameoffset_t fr_off = end_frame - frame;
2979 if (prev_t && prev_t->pulse() > 0.0) {
2980 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (future_map, prev_t->frame() - 1));
2983 TempoSection* next_t = 0;
2984 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2985 TempoSection* t = 0;
2986 if ((*i)->is_tempo()) {
2987 t = static_cast<TempoSection*> (*i);
2988 if (t->frame() > ts->frame()) {
2994 /* minimum allowed measurement distance in frames */
2995 const framepos_t min_dframe = 2;
2997 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2998 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3000 double contribution = 0.0;
3002 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3003 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3006 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3008 const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
3009 const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
3013 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3015 if (prev_t->position_lock_style() == MusicTime) {
3016 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3017 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3019 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
3020 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3022 new_bpm = prev_t->beats_per_minute();
3025 /* prev to prev is irrelevant */
3027 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3028 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3030 new_bpm = prev_t->beats_per_minute();
3035 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3036 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3038 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
3039 / (double) ((end_frame) - prev_to_prev_t->frame()));
3041 new_bpm = prev_t->beats_per_minute();
3044 /* prev_to_prev_t is irrelevant */
3046 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3047 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3049 new_bpm = prev_t->beats_per_minute();
3055 double frame_ratio = 1.0;
3056 double pulse_ratio = 1.0;
3057 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
3059 if (prev_to_prev_t) {
3060 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3061 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3063 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3064 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3067 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3068 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3070 pulse_ratio = (start_pulse / end_pulse);
3072 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
3075 /* don't clamp and proceed here.
3076 testing has revealed that this can go negative,
3077 which is an entirely different thing to just being too low.
3079 if (new_bpm < 0.5) {
3082 new_bpm = min (new_bpm, (double) 1000.0);
3083 prev_t->set_beats_per_minute (new_bpm);
3084 recompute_tempi (future_map);
3085 recompute_meters (future_map);
3087 if (check_solved (future_map)) {
3088 ts->set_beats_per_minute (new_bpm);
3089 recompute_tempi (_metrics);
3090 recompute_meters (_metrics);
3094 Metrics::const_iterator d = future_map.begin();
3095 while (d != future_map.end()) {
3100 MetricPositionChanged (); // Emit Signal
3103 /** Returns the exact beat subdivision closest to the supplied frame, possibly returning a negative value.
3104 * @param frame The session frame position.
3105 * @param sub_num The requested beat subdivision to use when rounding the frame position.
3106 * @return The beat position of the supplied frame.
3107 * If the supplied frame lies before the first meter, the return will be negative.
3108 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
3110 * This function uses both tempo and meter.
3113 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num)
3115 Glib::Threads::RWLock::ReaderLock lm (lock);
3117 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3121 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num)
3123 double beat = beat_at_frame_locked (metrics, frame);
3126 beat = floor (beat) + (floor (((beat - floor (beat)) * (double) sub_num) + 0.5) / sub_num);
3127 } else if (sub_num == 1) {
3129 beat = floor (beat + 0.5);
3130 } else if (sub_num == -1) {
3132 Timecode::BBT_Time bbt = bbt_at_beat_locked (metrics, beat);
3136 const double prev_b = beat_at_bbt_locked (_metrics, bbt);
3138 const double next_b = beat_at_bbt_locked (_metrics, bbt);
3140 if ((beat - prev_b) > (next_b - prev_b) / 2.0) {
3150 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num)
3152 Glib::Threads::RWLock::ReaderLock lm (lock);
3154 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3158 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num)
3160 double qn = quarter_note_at_frame_locked (metrics, frame);
3163 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3164 } else if (sub_num == 1) {
3165 /* snap to quarter note */
3166 qn = floor (qn + 0.5);
3167 } else if (sub_num == -1) {
3169 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3173 const double prev_b = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
3175 const double next_b = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
3177 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3188 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3190 Glib::Threads::RWLock::ReaderLock lm (lock);
3192 const double tick_at_time = max (0.0, beat_at_frame_locked (_metrics, pos)) * BBT_Time::ticks_per_beat;
3193 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
3194 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
3196 return frame_at_beat_locked (_metrics, total_beats);
3200 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3202 return round_to_type (fr, dir, Bar);
3206 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3208 return round_to_type (fr, dir, Beat);
3212 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3214 Glib::Threads::RWLock::ReaderLock lm (lock);
3215 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_frame_locked (_metrics, fr)) * BBT_Time::ticks_per_beat);
3216 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3217 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3219 ticks -= beats * BBT_Time::ticks_per_beat;
3222 /* round to next (or same iff dir == RoundUpMaybe) */
3224 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3226 if (mod == 0 && dir == RoundUpMaybe) {
3227 /* right on the subdivision, which is fine, so do nothing */
3229 } else if (mod == 0) {
3230 /* right on the subdivision, so the difference is just the subdivision ticks */
3231 ticks += ticks_one_subdivisions_worth;
3234 /* not on subdivision, compute distance to next subdivision */
3236 ticks += ticks_one_subdivisions_worth - mod;
3239 if (ticks >= BBT_Time::ticks_per_beat) {
3240 ticks -= BBT_Time::ticks_per_beat;
3242 } else if (dir < 0) {
3244 /* round to previous (or same iff dir == RoundDownMaybe) */
3246 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3248 if (difference == 0 && dir == RoundDownAlways) {
3249 /* right on the subdivision, but force-rounding down,
3250 so the difference is just the subdivision ticks */
3251 difference = ticks_one_subdivisions_worth;
3254 if (ticks < difference) {
3255 ticks = BBT_Time::ticks_per_beat - ticks;
3257 ticks -= difference;
3261 /* round to nearest */
3264 /* compute the distance to the previous and next subdivision */
3266 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3268 /* closer to the next subdivision, so shift forward */
3270 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3272 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3274 if (ticks > BBT_Time::ticks_per_beat) {
3276 ticks -= BBT_Time::ticks_per_beat;
3277 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3280 } else if (rem > 0) {
3282 /* closer to previous subdivision, so shift backward */
3286 /* can't go backwards past zero, so ... */
3289 /* step back to previous beat */
3291 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3292 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3294 ticks = lrint (ticks - rem);
3295 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3298 /* on the subdivision, do nothing */
3302 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
3308 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3310 Glib::Threads::RWLock::ReaderLock lm (lock);
3311 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_frame_locked (_metrics, fr)) * BBT_Time::ticks_per_beat);
3312 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3313 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3315 ticks -= beats * BBT_Time::ticks_per_beat;
3318 /* round to next (or same iff dir == RoundUpMaybe) */
3320 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3322 if (mod == 0 && dir == RoundUpMaybe) {
3323 /* right on the subdivision, which is fine, so do nothing */
3325 } else if (mod == 0) {
3326 /* right on the subdivision, so the difference is just the subdivision ticks */
3327 ticks += ticks_one_subdivisions_worth;
3330 /* not on subdivision, compute distance to next subdivision */
3332 ticks += ticks_one_subdivisions_worth - mod;
3335 if (ticks >= BBT_Time::ticks_per_beat) {
3336 ticks -= BBT_Time::ticks_per_beat;
3338 } else if (dir < 0) {
3340 /* round to previous (or same iff dir == RoundDownMaybe) */
3342 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3344 if (difference == 0 && dir == RoundDownAlways) {
3345 /* right on the subdivision, but force-rounding down,
3346 so the difference is just the subdivision ticks */
3347 difference = ticks_one_subdivisions_worth;
3350 if (ticks < difference) {
3351 ticks = BBT_Time::ticks_per_beat - ticks;
3353 ticks -= difference;
3357 /* round to nearest */
3360 /* compute the distance to the previous and next subdivision */
3362 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3364 /* closer to the next subdivision, so shift forward */
3366 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3368 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3370 if (ticks > BBT_Time::ticks_per_beat) {
3372 ticks -= BBT_Time::ticks_per_beat;
3373 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3376 } else if (rem > 0) {
3378 /* closer to previous subdivision, so shift backward */
3382 /* can't go backwards past zero, so ... */
3385 /* step back to previous beat */
3387 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3388 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3390 ticks = lrint (ticks - rem);
3391 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3394 /* on the subdivision, do nothing */
3398 const framepos_t ret_frame = frame_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
3404 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3406 Glib::Threads::RWLock::ReaderLock lm (lock);
3408 const double beat_at_framepos = max (0.0, beat_at_frame_locked (_metrics, frame));
3409 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3414 /* find bar previous to 'frame' */
3417 return frame_at_bbt_locked (_metrics, bbt);
3419 } else if (dir > 0) {
3420 /* find bar following 'frame' */
3424 return frame_at_bbt_locked (_metrics, bbt);
3426 /* true rounding: find nearest bar */
3427 framepos_t raw_ft = frame_at_bbt_locked (_metrics, bbt);
3430 framepos_t prev_ft = frame_at_bbt_locked (_metrics, bbt);
3432 framepos_t next_ft = frame_at_bbt_locked (_metrics, bbt);
3434 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3445 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3446 } else if (dir > 0) {
3447 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3449 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3458 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3459 framepos_t lower, framepos_t upper)
3461 Glib::Threads::RWLock::ReaderLock lm (lock);
3462 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3464 /* although the map handles negative beats, bbt doesn't. */
3469 if (frame_at_beat_locked (_metrics, cnt) >= upper) {
3473 while (pos < upper) {
3474 pos = frame_at_beat_locked (_metrics, cnt);
3475 const TempoSection tempo = tempo_section_at_frame_locked (_metrics, pos);
3476 const MeterSection meter = meter_section_at_frame_locked (_metrics, pos);
3477 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3478 points.push_back (BBTPoint (meter, tempo_at_frame_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3484 TempoMap::tempo_section_at_frame (framepos_t frame) const
3486 Glib::Threads::RWLock::ReaderLock lm (lock);
3487 return tempo_section_at_frame_locked (_metrics, frame);
3491 TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3493 TempoSection* prev = 0;
3497 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3499 if ((*i)->is_tempo()) {
3500 t = static_cast<TempoSection*> (*i);
3504 if (prev && t->frame() > frame) {
3514 abort(); /*NOTREACHED*/
3521 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3523 TempoSection* prev_t = 0;
3524 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3528 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3529 if ((*i)->is_tempo()) {
3530 t = static_cast<TempoSection*> (*i);
3531 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3541 /* don't use this to calculate length (the tempo is only correct for this frame).
3542 do that stuff based on the beat_at_frame and frame_at_beat api
3545 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3547 Glib::Threads::RWLock::ReaderLock lm (lock);
3549 const TempoSection* ts_at = 0;
3550 const TempoSection* ts_after = 0;
3551 Metrics::const_iterator i;
3554 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3556 if ((*i)->is_tempo()) {
3557 t = static_cast<TempoSection*> (*i);
3561 if (ts_at && (*i)->frame() > frame) {
3570 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate) * ts_at->note_type());
3572 /* must be treated as constant tempo */
3573 return ts_at->frames_per_beat (_frame_rate);
3577 TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3579 Metrics::const_iterator i;
3580 MeterSection* prev = 0;
3584 for (i = metrics.begin(); i != metrics.end(); ++i) {
3586 if (!(*i)->is_tempo()) {
3587 m = static_cast<MeterSection*> (*i);
3589 if (prev && (*i)->frame() > frame) {
3599 abort(); /*NOTREACHED*/
3607 TempoMap::meter_section_at_frame (framepos_t frame) const
3609 Glib::Threads::RWLock::ReaderLock lm (lock);
3610 return meter_section_at_frame_locked (_metrics, frame);
3614 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3616 MeterSection* prev_m = 0;
3618 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3620 if (!(*i)->is_tempo()) {
3621 m = static_cast<MeterSection*> (*i);
3622 if (prev_m && m->beat() > beat) {
3633 TempoMap::meter_section_at_beat (double beat) const
3635 Glib::Threads::RWLock::ReaderLock lm (lock);
3636 return meter_section_at_beat_locked (_metrics, beat);
3640 TempoMap::meter_at_frame (framepos_t frame) const
3642 TempoMetric m (metric_at (frame));
3647 TempoMap::fix_legacy_session ()
3649 MeterSection* prev_m = 0;
3650 TempoSection* prev_t = 0;
3652 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3656 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3657 if (!m->movable()) {
3658 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3662 m->set_position_lock_style (AudioTime);
3667 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3668 + (m->bbt().beats - 1)
3669 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3671 m->set_beat (start);
3672 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3673 + (m->bbt().beats - 1)
3674 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3675 m->set_pulse (start_beat / prev_m->note_divisor());
3678 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3684 if (!t->movable()) {
3687 t->set_position_lock_style (AudioTime);
3693 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3694 + (t->legacy_bbt().beats - 1)
3695 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3697 t->set_pulse (beat / prev_m->note_divisor());
3699 /* really shouldn't happen but.. */
3700 t->set_pulse (beat / 4.0);
3709 TempoMap::get_state ()
3711 Metrics::const_iterator i;
3712 XMLNode *root = new XMLNode ("TempoMap");
3715 Glib::Threads::RWLock::ReaderLock lm (lock);
3716 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3717 root->add_child_nocopy ((*i)->get_state());
3725 TempoMap::set_state (const XMLNode& node, int /*version*/)
3728 Glib::Threads::RWLock::WriterLock lm (lock);
3731 XMLNodeConstIterator niter;
3732 Metrics old_metrics (_metrics);
3735 nlist = node.children();
3737 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3738 XMLNode* child = *niter;
3740 if (child->name() == TempoSection::xml_state_node_name) {
3743 TempoSection* ts = new TempoSection (*child);
3744 _metrics.push_back (ts);
3747 catch (failed_constructor& err){
3748 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3749 _metrics = old_metrics;
3750 old_metrics.clear();
3754 } else if (child->name() == MeterSection::xml_state_node_name) {
3757 MeterSection* ms = new MeterSection (*child);
3758 _metrics.push_back (ms);
3761 catch (failed_constructor& err) {
3762 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3763 _metrics = old_metrics;
3764 old_metrics.clear();
3770 if (niter == nlist.end()) {
3771 MetricSectionSorter cmp;
3772 _metrics.sort (cmp);
3775 /* check for legacy sessions where bbt was the base musical unit for tempo */
3776 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3778 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3779 if (t->legacy_bbt().bars != 0) {
3780 fix_legacy_session();
3787 /* check for multiple tempo/meters at the same location, which
3788 ardour2 somehow allowed.
3791 Metrics::iterator prev = _metrics.end();
3792 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3793 if (prev != _metrics.end()) {
3795 MeterSection* prev_m;
3797 TempoSection* prev_t;
3798 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3799 if (prev_m->pulse() == ms->pulse()) {
3800 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3801 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3804 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3805 if (prev_t->pulse() == ts->pulse()) {
3806 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3807 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3815 recompute_map (_metrics);
3817 Metrics::const_iterator d = old_metrics.begin();
3818 while (d != old_metrics.end()) {
3822 old_metrics.clear ();
3825 PropertyChanged (PropertyChange ());
3831 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3833 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3834 const MeterSection* m;
3835 const TempoSection* t;
3836 const TempoSection* prev_t = 0;
3838 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3840 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3841 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3842 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3843 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3845 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3846 o << "calculated : " << prev_t->tempo_at_pulse (t->pulse()) * prev_t->note_type() << " | " << prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << " | " << prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
3849 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3850 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3851 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3854 o << "------" << std::endl;
3858 TempoMap::n_tempos() const
3860 Glib::Threads::RWLock::ReaderLock lm (lock);
3863 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3864 if ((*i)->is_tempo()) {
3873 TempoMap::n_meters() const
3875 Glib::Threads::RWLock::ReaderLock lm (lock);
3878 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3879 if (!(*i)->is_tempo()) {
3888 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3891 Glib::Threads::RWLock::WriterLock lm (lock);
3892 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3893 if ((*i)->frame() >= where && (*i)->movable ()) {
3894 (*i)->set_frame ((*i)->frame() + amount);
3898 /* now reset the BBT time of all metrics, based on their new
3899 * audio time. This is the only place where we do this reverse
3903 Metrics::iterator i;
3904 const MeterSection* meter;
3905 const TempoSection* tempo;
3909 meter = &first_meter ();
3910 tempo = &first_tempo ();
3915 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3918 MetricSection* prev = 0;
3920 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3923 //TempoMetric metric (*meter, *tempo);
3924 MeterSection* ms = const_cast<MeterSection*>(meter);
3925 TempoSection* ts = const_cast<TempoSection*>(tempo);
3928 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3932 ts->set_pulse (t->pulse());
3934 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3935 ts->set_pulse (m->pulse());
3937 ts->set_frame (prev->frame());
3941 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3942 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3943 ms->set_beat (start);
3944 ms->set_pulse (m->pulse());
3946 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3950 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3951 pair<double, BBT_Time> start = make_pair (beat, bbt_at_beat_locked (_metrics, beat));
3952 ms->set_beat (start);
3953 ms->set_pulse (t->pulse());
3955 ms->set_frame (prev->frame());
3959 // metric will be at frames=0 bbt=1|1|0 by default
3960 // which is correct for our purpose
3963 // cerr << bbt << endl;
3965 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3969 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3971 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3972 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3973 bbt = bbt_at_frame_locked (_metrics, m->frame());
3975 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3981 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3982 /* round up to next beat */
3988 if (bbt.beats != 1) {
3989 /* round up to next bar */
3994 pair<double, BBT_Time> start = make_pair (max (0.0, beat_at_frame_locked (_metrics, m->frame())), bbt);
3995 m->set_beat (start);
3996 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3998 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
4000 fatal << _("programming error: unhandled MetricSection type") << endmsg;
4001 abort(); /*NOTREACHED*/
4007 recompute_map (_metrics);
4011 PropertyChanged (PropertyChange ());
4014 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4018 std::list<MetricSection*> metric_kill_list;
4020 TempoSection* last_tempo = NULL;
4021 MeterSection* last_meter = NULL;
4022 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4023 bool meter_after = false; // is there a meter marker likewise?
4025 Glib::Threads::RWLock::WriterLock lm (lock);
4026 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4027 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4028 metric_kill_list.push_back(*i);
4029 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4032 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4036 else if ((*i)->frame() >= where) {
4037 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4038 (*i)->set_frame ((*i)->frame() - amount);
4039 if ((*i)->frame() == where) {
4040 // marker was immediately after end of range
4041 tempo_after = dynamic_cast<TempoSection*> (*i);
4042 meter_after = dynamic_cast<MeterSection*> (*i);
4048 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4049 if (last_tempo && !tempo_after) {
4050 metric_kill_list.remove(last_tempo);
4051 last_tempo->set_frame(where);
4054 if (last_meter && !meter_after) {
4055 metric_kill_list.remove(last_meter);
4056 last_meter->set_frame(where);
4060 //remove all the remaining metrics
4061 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4062 _metrics.remove(*i);
4067 recompute_map (_metrics);
4070 PropertyChanged (PropertyChange ());
4074 /** Add some (fractional) beats to a session frame position, and return the result in frames.
4075 * pos can be -ve, if required.
4078 TempoMap::framepos_plus_beats (framepos_t frame, Evoral::Beats beats) const
4080 Glib::Threads::RWLock::ReaderLock lm (lock);
4082 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, frame) + beats.to_double());
4085 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4087 Glib::Threads::RWLock::ReaderLock lm (lock);
4089 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, frame) + beats.to_double());
4092 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
4094 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
4096 Glib::Threads::RWLock::ReaderLock lm (lock);
4098 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) - beats.to_double());
4101 /** Add the BBT interval op to pos and return the result */
4103 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4105 Glib::Threads::RWLock::ReaderLock lm (lock);
4107 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos));
4108 pos_bbt.ticks += op.ticks;
4109 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4111 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4113 pos_bbt.beats += op.beats;
4114 /* the meter in effect will start on the bar */
4115 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();
4116 while (pos_bbt.beats >= divisions_per_bar + 1) {
4118 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4119 pos_bbt.beats -= divisions_per_bar;
4121 pos_bbt.bars += op.bars;
4123 return frame_at_bbt_locked (_metrics, pos_bbt);
4126 /** Count the number of beats that are equivalent to distance when going forward,
4130 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
4132 Glib::Threads::RWLock::ReaderLock lm (lock);
4134 return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos));
4138 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4140 Glib::Threads::RWLock::ReaderLock lm (lock);
4142 return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos));
4145 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4151 operator<< (std::ostream& o, const Meter& m) {
4152 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4156 operator<< (std::ostream& o, const Tempo& t) {
4157 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
4161 operator<< (std::ostream& o, const MetricSection& section) {
4163 o << "MetricSection @ " << section.frame() << ' ';
4165 const TempoSection* ts;
4166 const MeterSection* ms;
4168 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4169 o << *((const Tempo*) ts);
4170 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4171 o << *((const Meter*) ms);