diff options
-rw-r--r-- | gtk2_ardour/editor_drag.cc | 10 | ||||
-rw-r--r-- | gtk2_ardour/editor_markers.cc | 9 | ||||
-rw-r--r-- | gtk2_ardour/editor_mouse.cc | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/tempo.h | 26 | ||||
-rw-r--r-- | libs/ardour/tempo.cc | 253 |
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 */ |