summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/editor_drag.cc10
-rw-r--r--gtk2_ardour/editor_markers.cc9
-rw-r--r--gtk2_ardour/editor_mouse.cc2
-rw-r--r--libs/ardour/ardour/tempo.h26
-rw-r--r--libs/ardour/tempo.cc253
5 files changed, 257 insertions, 43 deletions
diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc
index 63fff4e463..53f1e74232 100644
--- a/gtk2_ardour/editor_drag.cc
+++ b/gtk2_ardour/editor_drag.cc
@@ -3201,7 +3201,9 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
/* round bbt to bars */
map.round_bbt (bbt, -1);
- if (bbt.bars > _real_section->bbt().bars) {
+ if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
+ _editor->session()->tempo_map().gui_dilate_tempo (_real_section, pf);
+ } else if (bbt.bars > _real_section->bbt().bars) {
const double pulse = _real_section->pulse() + (_real_section->note_divisor() / _real_section->divisions_per_bar());
_editor->session()->tempo_map().gui_move_meter (_real_section, pulse);
} else if (bbt.bars < _real_section->bbt().bars) {
@@ -3210,7 +3212,11 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move)
_editor->session()->tempo_map().gui_move_meter (_real_section, pulse);
}
} else {
- _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
+ if (Keyboard::modifier_state_equals (event->button.state, ArdourKeyboard::constraint_modifier ())) {
+ _editor->session()->tempo_map().gui_dilate_tempo (_real_section, pf);
+ } else {
+ _editor->session()->tempo_map().gui_move_meter (_real_section, pf);
+ }
}
_marker->set_position (pf);
show_verbose_cursor_time (_real_section->frame());
diff --git a/gtk2_ardour/editor_markers.cc b/gtk2_ardour/editor_markers.cc
index 74e35a246b..9664d6f65f 100644
--- a/gtk2_ardour/editor_markers.cc
+++ b/gtk2_ardour/editor_markers.cc
@@ -1426,9 +1426,12 @@ Editor::toggle_tempo_type ()
begin_reversible_command (_("change tempo type"));
XMLNode &before = _session->tempo_map().get_state();
TempoSection* tsp = &tm->tempo();
- _session->tempo_map().replace_tempo (*tsp, Tempo (tsp->beats_per_minute(), tsp->note_type())
- , (tsp->position_lock_style() == MusicTime) ? tsp->pulse() : tsp->frame()
- , (tsp->type() == TempoSection::Ramp) ? TempoSection::Constant : TempoSection::Ramp);
+ if (tsp->position_lock_style() == AudioTime) {
+ _session->tempo_map().replace_tempo (*tsp, Tempo (tsp->beats_per_minute(), tsp->note_type()), tsp->frame(), (tsp->type() == TempoSection::Ramp) ? TempoSection::Constant : TempoSection::Ramp);
+ } else {
+ _session->tempo_map().replace_tempo (*tsp, Tempo (tsp->beats_per_minute(), tsp->note_type()), tsp->pulse(), (tsp->type() == TempoSection::Ramp) ? TempoSection::Constant : TempoSection::Ramp);
+ }
+
XMLNode &after = _session->tempo_map().get_state();
_session->add_command(new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
commit_reversible_command ();
diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc
index 49aaac68bf..4a4bfe1e1e 100644
--- a/gtk2_ardour/editor_mouse.cc
+++ b/gtk2_ardour/editor_mouse.cc
@@ -697,7 +697,7 @@ Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemT
new MeterMarkerDrag (
this,
item,
- Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
+ Keyboard::modifier_state_equals (event->button.state, Keyboard::CopyModifier)
),
event
);
diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h
index c8c6af5247..25180b8eb3 100644
--- a/libs/ardour/ardour/tempo.h
+++ b/libs/ardour/ardour/tempo.h
@@ -178,26 +178,29 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
};
TempoSection (const double& beat, double qpm, double note_type, Type tempo_type)
- : MetricSection (beat), Tempo (qpm, note_type), _type (tempo_type), _c_func (0.0), _active (true) {}
+ : MetricSection (beat), Tempo (qpm, note_type), _type (tempo_type), _c_func (0.0), _active (true), _locked_to_meter (false) {}
TempoSection (framepos_t frame, double qpm, double note_type, Type tempo_type)
- : MetricSection (frame), Tempo (qpm, note_type), _type (tempo_type), _c_func (0.0), _active (true) {}
+ : MetricSection (frame), Tempo (qpm, note_type), _type (tempo_type), _c_func (0.0), _active (true), _locked_to_meter (false) {}
TempoSection (const XMLNode&);
static const std::string xml_state_node_name;
XMLNode& get_state() const;
- bool active () const { return _active; }
- void set_active (bool yn) { _active = yn; }
+ double c_func () const { return _c_func; }
+ void set_c_func (double c_func) { _c_func = c_func; }
void set_type (Type type);
Type type () const { return _type; }
- double c_func () const { return _c_func; }
- void set_c_func (double c_func) { _c_func = c_func; }
+ bool active () const { return _active; }
+ void set_active (bool yn) { _active = yn; }
+
+ bool locked_to_meter () const { return _locked_to_meter; }
+ void set_locked_to_meter (bool yn) { _locked_to_meter = yn; }
- double tempo_at_frame (const framepos_t& frame, const framecnt_t& frame_rate) const;
- framepos_t frame_at_tempo (const double& ppm, const double& beat, const framecnt_t& frame_rate) const;
+ double tempo_at_frame (const frameoffset_t& frame, const framecnt_t& frame_rate) const;
+ frameoffset_t frame_at_tempo (const double& ppm, const double& beat, const framecnt_t& frame_rate) const;
double tempo_at_pulse (const double& pulse) const;
double pulse_at_tempo (const double& ppm, const framepos_t& frame, const framecnt_t& frame_rate) const;
@@ -212,8 +215,8 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
private:
- framecnt_t minute_to_frame (const double& time, const framecnt_t& frame_rate) const;
- double frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const;
+ frameoffset_t minute_to_frame (const double& time, const framecnt_t& frame_rate) const;
+ double frame_to_minute (const frameoffset_t& frame, const framecnt_t& frame_rate) const;
/* tempo ramp functions. zero-based with time in minutes,
* 'tick tempo' in ticks per minute and tempo in bpm.
@@ -242,6 +245,7 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo {
Type _type;
double _c_func;
bool _active;
+ bool _locked_to_meter;
Timecode::BBT_Time _legacy_bbt;
};
@@ -393,6 +397,7 @@ class LIBARDOUR_API TempoMap : public PBD::StatefulDestructible
void gui_move_meter (MeterSection*, const framepos_t& frame);
void gui_move_meter (MeterSection*, const double& pulse);
bool gui_change_tempo (TempoSection*, const Tempo& bpm);
+ void gui_dilate_tempo (MeterSection*, const framepos_t& frame);
bool can_solve_bbt (TempoSection* section, const Timecode::BBT_Time& bbt);
@@ -463,6 +468,7 @@ private:
const TempoSection& tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const;
const MeterSection& meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const;
const TempoSection& tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const;
+ const TempoSection& tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const;
const Tempo tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const;
bool check_solved (const Metrics& metrics, bool by_frame) const;
diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc
index c9176847c9..c9242d1f92 100644
--- a/libs/ardour/tempo.cc
+++ b/libs/ardour/tempo.cc
@@ -77,6 +77,7 @@ TempoSection::TempoSection (const XMLNode& node)
, Tempo (TempoMap::default_tempo())
, _c_func (0.0)
, _active (true)
+ , _locked_to_meter (false)
{
const XMLProperty *prop;
LocaleGuard lg;
@@ -163,6 +164,12 @@ TempoSection::TempoSection (const XMLNode& node)
} else {
set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
}
+
+ if ((prop = node.property ("locked-to-meter")) == 0) {
+ set_locked_to_meter (false);
+ } else {
+ set_locked_to_meter (string_is_affirmative (prop->value()));
+ }
}
XMLNode&
@@ -186,6 +193,7 @@ TempoSection::get_state() const
root->add_property ("active", buf);
root->add_property ("tempo-type", enum_2_string (_type));
root->add_property ("lock-style", enum_2_string (position_lock_style()));
+ root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
return *root;
}
@@ -199,14 +207,14 @@ TempoSection::set_type (Type type)
/** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
*/
double
-TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
+TempoSection::tempo_at_frame (const frameoffset_t& f, const framecnt_t& frame_rate) const
{
if (_type == Constant || _c_func == 0.0) {
return pulses_per_minute();
}
- return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
+ return pulse_tempo_at_time (frame_to_minute (f - (frameoffset_t) frame(), frame_rate));
}
/** returns the zero-based frame (relative to session)
@@ -214,7 +222,7 @@ TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate)
beat b is only used for constant tempos.
note that the tempo map may have multiple such values.
*/
-framepos_t
+frameoffset_t
TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
{
if (_type == Constant || _c_func == 0.0) {
@@ -308,7 +316,7 @@ The integral over t of our Tempo function (the beat function, which is the durat
b(t) = T0(e^(ct) - 1) / c
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:
-t(b) = log((cb / T0) + 1) / c
+t(b) = log((c.b / T0) + 1) / c
The time t at which Tempo T occurs is a as above:
t(T) = log(T / T0) / c
@@ -372,30 +380,30 @@ TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end
return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
}
-framecnt_t
+frameoffset_t
TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
{
return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
}
double
-TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
+TempoSection::frame_to_minute (const frameoffset_t& frame, const framecnt_t& frame_rate) const
{
return (frame / (double) frame_rate) / 60.0;
}
/* position function */
double
-TempoSection::a_func (double end_bpm, double c_func) const
+TempoSection::a_func (double end_ppm, double c_func) const
{
- return log (end_bpm / pulses_per_minute()) / c_func;
+ return log (end_ppm / pulses_per_minute()) / c_func;
}
/*function constant*/
double
-TempoSection::c_func (double end_bpm, double end_time) const
+TempoSection::c_func (double end_ppm, double end_time) const
{
- return log (end_bpm / pulses_per_minute()) / end_time;
+ return log (end_ppm / pulses_per_minute()) / end_time;
}
/* tempo in ppm at time in minutes */
@@ -676,6 +684,7 @@ TempoMap::remove_tempo_locked (const TempoSection& tempo)
if (dynamic_cast<TempoSection*> (*i) != 0) {
if (tempo.frame() == (*i)->frame()) {
if ((*i)->movable()) {
+ delete (*i);
_metrics.erase (i);
return true;
}
@@ -706,14 +715,28 @@ TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
}
bool
-TempoMap::remove_meter_locked (const MeterSection& tempo)
+TempoMap::remove_meter_locked (const MeterSection& meter)
{
Metrics::iterator i;
for (i = _metrics.begin(); i != _metrics.end(); ++i) {
+ TempoSection* t = 0;
+ if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if (meter.frame() == (*i)->frame()) {
+ if (t->locked_to_meter()) {
+ delete (*i);
+ _metrics.erase (i);
+ break;
+ }
+ }
+ }
+ }
+
+ for (i = _metrics.begin(); i != _metrics.end(); ++i) {
if (dynamic_cast<MeterSection*> (*i) != 0) {
- if (tempo.frame() == (*i)->frame()) {
+ if (meter.frame() == (*i)->frame()) {
if ((*i)->movable()) {
+ delete (*i);
_metrics.erase (i);
return true;
}
@@ -782,6 +805,7 @@ TempoMap::do_insert (MetricSection* section)
}
need_add = false;
} else {
+ delete (*i);
_metrics.erase (i);
}
break;
@@ -806,6 +830,7 @@ TempoMap::do_insert (MetricSection* section)
(*i)->set_position_lock_style (AudioTime);
need_add = false;
} else {
+ delete (*i);
_metrics.erase (i);
}
@@ -937,6 +962,7 @@ TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, AR
if (recompute) {
solve_map (_metrics, t, t->pulse());
+ recompute_meters (_metrics);
}
return t;
@@ -951,6 +977,7 @@ TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute
if (recompute) {
solve_map (_metrics, t, t->frame());
+ recompute_meters (_metrics);
}
return t;
@@ -1082,12 +1109,16 @@ MeterSection*
TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
{
MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
-
+ TempoSection* t = 0;
double pulse = pulse_at_frame_locked (_metrics, frame);
new_meter->set_pulse (pulse);
do_insert (new_meter);
+ /* add meter-locked tempo */
+ t = add_tempo_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
+ t->set_locked_to_meter (true);
+
if (recompute) {
solve_map (_metrics, new_meter, frame);
}
@@ -1259,9 +1290,11 @@ TempoMap::recompute_tempos (Metrics& metrics)
continue;
}
if (!t->movable()) {
- t->set_pulse (0.0);
- prev_t = t;
- continue;
+ if (!prev_t) {
+ t->set_pulse (0.0);
+ prev_t = t;
+ continue;
+ }
}
if (prev_t) {
if (t->position_lock_style() == AudioTime) {
@@ -1920,7 +1953,6 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t
}
if (check_solved (imaginary, true)) {
- recompute_meters (imaginary);
return true;
}
@@ -1936,7 +1968,6 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t
}
if (check_solved (imaginary, true)) {
- recompute_meters (imaginary);
return true;
}
@@ -1995,7 +2026,6 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pu
}
if (check_solved (imaginary, false)) {
- recompute_meters (imaginary);
return true;
}
@@ -2011,7 +2041,6 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pu
}
if (check_solved (imaginary, false)) {
- recompute_meters (imaginary);
return true;
}
@@ -2029,7 +2058,6 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t
return;
}
MeterSection* prev_m = 0;
-
if (!section->movable()) {
/* lock the first tempo to our first meter */
if (!set_active_tempos (imaginary, frame)) {
@@ -2053,11 +2081,33 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t
}
}
+ TempoSection* meter_locked_tempo = 0;
+
+ if ((meter_locked_tempo = const_cast<TempoSection*>(&tempo_section_at_locked (imaginary, section->frame())))->frame() == section->frame()) {
+ if (meter_locked_tempo->locked_to_meter()) {
+ std::cout << "locked to meter " << std::endl;
+ Metrics future_map;
+ TempoSection* new_section = copy_metrics_and_point (future_map, meter_locked_tempo);
+
+ new_section->set_frame (frame);
+ new_section->set_active (true);
+
+ if (solve_map (future_map, new_section, frame)) {
+ meter_locked_tempo->set_frame (frame);
+ meter_locked_tempo->set_active (true);
+ solve_map (imaginary, meter_locked_tempo, frame);
+ } else {
+ return;
+ }
+ } else {
+ meter_locked_tempo = 0;
+ }
+ }
+
for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
MeterSection* m;
if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
if (m == section){
- double new_pulse = 0.0;
if (prev_m && section->movable()) {
const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
if (beats + prev_m->beat() < section->beat()) {
@@ -2067,18 +2117,25 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t
this is what the user would actually want.
here we set the frame/pulse corresponding to its musical position.
*/
- new_pulse = ((section->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
+ const double new_pulse = ((section->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
section->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
section->set_pulse (new_pulse);
+ if (meter_locked_tempo) {
+ meter_locked_tempo->set_frame (section->frame());
+ /* XX need to fake the pulse and prevent it from changing */
+ //meter_locked_tempo->set_pulse (pulse_at_frame_locked (imaginary, section->frame()));
+ }
break;
+ } else {
+ section->set_pulse (pulse_at_frame_locked (imaginary, frame));
}
- new_pulse = pulse_at_frame_locked (imaginary, frame);
} else {
pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
section->set_beat (b_bbt);
+ section->set_pulse (0.0);
+
}
section->set_frame (frame);
- section->set_pulse (new_pulse);
break;
}
@@ -2157,6 +2214,7 @@ TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
ret->set_pulse (t->pulse());
}
+ ret->set_c_func (t->c_func());
ret->set_active (t->active());
ret->set_movable (t->movable());
copy.push_back (ret);
@@ -2170,6 +2228,7 @@ TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
cp->set_pulse (t->pulse());
}
+ cp->set_c_func (t->c_func());
cp->set_active (t->active());
cp->set_movable (t->movable());
copy.push_back (cp);
@@ -2202,8 +2261,8 @@ TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
new_section = copy_metrics_and_point (copy, ts);
}
- double const beat = bbt_to_beats_locked (copy, bbt);
- bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
+ const double beat = bbt_to_beats_locked (copy, bbt);
+ const bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
Metrics::const_iterator d = copy.begin();
while (d != copy.end()) {
@@ -2277,6 +2336,7 @@ TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
TempoSection* new_section = copy_metrics_and_point (future_map, ts);
if (solve_map (future_map, new_section, frame)) {
solve_map (_metrics, ts, frame);
+ recompute_meters (_metrics);
}
}
@@ -2298,6 +2358,7 @@ TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
TempoSection* new_section = copy_metrics_and_point (future_map, ts);
if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
+ recompute_meters (_metrics);
}
}
@@ -2361,6 +2422,126 @@ TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
return can_solve;
}
+void
+TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
+{
+ Metrics future_map;
+ TempoSection* ts = 0;
+ {
+ Glib::Threads::RWLock::WriterLock lm (lock);
+ ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
+ TempoSection* prev_t = copy_metrics_and_point (future_map, ts);
+ TempoSection* prev_to_prev_t = 0;
+ const frameoffset_t fr_off = frame - ms->frame();
+ double new_bpm = 0.0;
+
+ if (prev_t) {
+ prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
+ }
+
+ /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
+ constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
+ */
+ if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
+ if (prev_t->position_lock_style() == MusicTime) {
+ if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+ /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
+ const double contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
+ const frameoffset_t frame_contribution = contribution * (double) fr_off;
+ const frameoffset_t prev_t_frame_contribution = fr_off - frame_contribution;
+ new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
+ / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
+
+ } else {
+ /* prev to prev is irrelevant */
+ const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
+ const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
+
+ if (frame_pulse != prev_t->pulse()) {
+ new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
+ } else {
+ new_bpm = prev_t->beats_per_minute();
+ }
+ }
+ } else {
+ /* AudioTime */
+ if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+ /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
+ const double contribution = (prev_t->frame() - prev_to_prev_t->frame())
+ / (double) (ms->frame() - prev_to_prev_t->frame());
+ const frameoffset_t frame_contribution = contribution * (double) fr_off;
+ const frameoffset_t prev_t_frame_contribution = fr_off - frame_contribution;
+ new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
+ / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
+ } else {
+ /* prev_to_prev_t is irrelevant */
+
+ if (frame != prev_t->frame()) {
+ new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
+ } else {
+ new_bpm = prev_t->beats_per_minute();
+ }
+ }
+ }
+ } else if (prev_t->c_func() < 0.0) {
+ if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+ const double contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
+ const frameoffset_t frame_contribution = contribution * (double) fr_off;
+ new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
+ } else {
+ /* prev_to_prev_t is irrelevant */
+ new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
+ }
+
+ const double diff = prev_t->beats_per_minute() - new_bpm;
+ if (diff > -0.1 && diff < 0.1) {
+ new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
+ }
+
+ } else if (prev_t->c_func() > 0.0) {
+ if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
+ const double contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
+ const frameoffset_t frame_contribution = contribution * (double) fr_off;
+ new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
+ } else {
+ /* prev_to_prev_t is irrelevant */
+ new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
+ }
+ /* limits - a bit clunky, but meh */
+ const double diff = prev_t->beats_per_minute() - new_bpm;
+ if (diff > -0.1 && diff < 0.1) {
+ new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
+ }
+ }
+
+ prev_t->set_beats_per_minute (new_bpm);
+ recompute_tempos (future_map);
+
+ if (check_solved (future_map, true)) {
+
+ prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
+ prev_t->set_beats_per_minute (new_bpm);
+ recompute_tempos (_metrics);
+
+ if (ms->position_lock_style() == AudioTime) {
+ ms->set_frame (frame);
+ } else {
+ ms->set_pulse (prev_t->pulse_at_frame (frame, _frame_rate));
+ }
+ recompute_meters (_metrics);
+ }
+ }
+
+ Metrics::const_iterator d = future_map.begin();
+ while (d != future_map.end()) {
+ delete (*d);
+ ++d;
+ }
+
+ MetricPositionChanged (); // Emit Signal
+ return;
+}
+
framecnt_t
TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
{
@@ -2662,6 +2843,24 @@ TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& be
return *prev_t;
}
+const TempoSection&
+TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
+{
+ TempoSection* prev_t = 0;
+
+ for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
+ TempoSection* t;
+ if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ if (prev_t && t->pulse() > pulse) {
+ break;
+ }
+ prev_t = t;
+ }
+
+ }
+ return *prev_t;
+}
+
/* don't use this to calculate length (the tempo is only correct for this frame).
do that stuff based on the beat_at_frame and frame_at_beat api
*/