From 8b4f5dcd5f6a570ef920fe77c4dba5242726db5f Mon Sep 17 00:00:00 2001 From: nick_m Date: Tue, 3 May 2016 22:15:10 +1000 Subject: [PATCH] Tempo ramps - reinstate cross-dragging of music-locked meters, various bug fixes. - revert failed frameoffset_t experiment - caclulate meters using bbt - fix tempo dilation when first tempo is ramped. --- gtk2_ardour/editor_drag.cc | 2 +- libs/ardour/ardour/tempo.h | 8 +- libs/ardour/tempo.cc | 168 +++++++++++++++++++++++++++---------- 3 files changed, 127 insertions(+), 51 deletions(-) diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 080e82d984..a686789253 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -3204,7 +3204,7 @@ MeterMarkerDrag::motion (GdkEvent* event, bool first_move) if (Keyboard::modifier_state_contains (event->button.state, ArdourKeyboard::constraint_modifier ())) { /* adjust previous tempo to match meter frame */ _editor->session()->tempo_map().gui_dilate_tempo (_real_section, pf); - } else if ((bbt.bars > _real_section->bbt().bars && pf > last_pointer_frame()) + } else if ((bbt.bars != _real_section->bbt().bars && pf > last_pointer_frame()) || (bbt.bars < _real_section->bbt().bars && pf < last_pointer_frame())) { /* move meter beat-based */ _editor->session()->tempo_map().gui_move_meter (_real_section, bbt); diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h index 87023e0cd8..889fde3e99 100644 --- a/libs/ardour/ardour/tempo.h +++ b/libs/ardour/ardour/tempo.h @@ -199,8 +199,8 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo { 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 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_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_pulse (const double& pulse) const; double pulse_at_tempo (const double& ppm, const framepos_t& frame, const framecnt_t& frame_rate) const; @@ -215,8 +215,8 @@ class LIBARDOUR_API TempoSection : public MetricSection, public Tempo { private: - 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; + framepos_t minute_to_frame (const double& time, const framecnt_t& frame_rate) const; + double frame_to_minute (const framepos_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. diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc index a9707c85ef..8af5ab3fbe 100644 --- a/libs/ardour/tempo.cc +++ b/libs/ardour/tempo.cc @@ -207,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 frameoffset_t& f, const framecnt_t& frame_rate) const +TempoSection::tempo_at_frame (const framepos_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 - (frameoffset_t) frame(), frame_rate)); + return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate)); } /** returns the zero-based frame (relative to session) @@ -222,7 +222,7 @@ TempoSection::tempo_at_frame (const frameoffset_t& f, const framecnt_t& frame_ra beat b is only used for constant tempos. note that the tempo map may have multiple such values. */ -frameoffset_t +framepos_t TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const { if (_type == Constant || _c_func == 0.0) { @@ -277,11 +277,11 @@ TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) falls. */ -frameoffset_t +framepos_t TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const { if (_type == Constant || _c_func == 0.0) { - return (frameoffset_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame(); + return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame(); } return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame(); @@ -380,14 +380,14 @@ 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)); } -frameoffset_t +framepos_t TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const { - return (frameoffset_t) floor ((time * 60.0 * (frameoffset_t) frame_rate) + 0.5); + return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5); } double -TempoSection::frame_to_minute (const frameoffset_t& frame, const framecnt_t& frame_rate) const +TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const { return (frame / (double) frame_rate) / 60.0; } @@ -762,7 +762,6 @@ TempoMap::do_insert (MetricSection* section) */ MeterSection* m = 0; if ((m = dynamic_cast(section)) != 0) { - //assert (m->bbt().ticks == 0); if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) { @@ -1321,7 +1320,12 @@ TempoMap::recompute_tempos (Metrics& metrics) prev_t->set_c_func (0.0); } -/* tempos must be positioned correctly */ +/* tempos must be positioned correctly. + the current approach is to use a meter's bbt time as its base position unit. + this means that a meter's beat may change, but its bbt may not. + an audio-locked meter requires a recomputation of pulse and beat (but not bbt), + while a music-locked meter requires recomputations of frame pulse and beat (but not bbt) +*/ void TempoMap::recompute_meters (Metrics& metrics) { @@ -1333,29 +1337,62 @@ TempoMap::recompute_meters (Metrics& metrics) if (meter->position_lock_style() == AudioTime) { double pulse = 0.0; pair b_bbt; - if (meter->movable()) { - b_bbt = make_pair (meter->beat(), meter->bbt()); - pulse = pulse_at_frame_locked (metrics, meter->frame()); + TempoSection* meter_locked_tempo = 0; + for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) { + TempoSection* t; + if ((t = dynamic_cast (*ii)) != 0) { + if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) { + meter_locked_tempo = t; + break; + } + } + } + + if (prev_m) { + const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar(); + if (beats + prev_m->beat() != meter->beat()) { + /* reordering caused a bbt change */ + b_bbt = make_pair (beats + prev_m->beat() + , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0)); + pulse = prev_m->pulse() + (beats / prev_m->note_divisor()); + + } else if (meter->movable()) { + b_bbt = make_pair (meter->beat(), meter->bbt()); + pulse = prev_m->pulse() + (beats / prev_m->note_divisor()); + } } else { b_bbt = make_pair (0.0, BBT_Time (1, 1, 0)); } + if (meter_locked_tempo) { + meter_locked_tempo->set_pulse (pulse); + recompute_tempos (metrics); + } meter->set_beat (b_bbt); meter->set_pulse (pulse); + } else { + /* MusicTime */ double pulse = 0.0; pair new_beat; if (prev_m) { - pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor()); - //new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt()); + const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar(); + if (beats + prev_m->beat() != meter->beat()) { + /* reordering caused a bbt change */ + new_beat = make_pair (beats + prev_m->beat() + , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0)); + } else { + new_beat = make_pair (beats + prev_m->beat(), meter->bbt()); + } + pulse = (beats / prev_m->note_divisor()) + prev_m->pulse(); } else { /* shouldn't happen - the first is audio-locked */ pulse = pulse_at_beat_locked (metrics, meter->beat()); new_beat = make_pair (meter->beat(), meter->bbt()); } - //meter->set_beat (new_beat); - meter->set_frame (frame_at_pulse_locked (metrics, pulse)); + meter->set_beat (new_beat); meter->set_pulse (pulse); + meter->set_frame (frame_at_pulse_locked (metrics, pulse)); } prev_m = meter; @@ -1866,7 +1903,8 @@ TempoMap::check_solved (const Metrics& metrics, bool by_frame) const if (prev_m && m->position_lock_style() == AudioTime) { TempoSection* t = const_cast(&tempo_section_at_locked (metrics, m->frame() - 1)); const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(); - const frameoffset_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate); + const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate); + if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) { return false; } @@ -1968,7 +2006,6 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t recompute_tempos (imaginary); } - recompute_meters (imaginary); if (check_solved (imaginary, true)) { return true; } @@ -1984,7 +2021,6 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t recompute_tempos (imaginary); } - recompute_meters (imaginary); if (check_solved (imaginary, true)) { return true; } @@ -2045,7 +2081,6 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pu recompute_tempos (imaginary); } - recompute_meters (imaginary); if (check_solved (imaginary, false)) { return true; } @@ -2061,7 +2096,6 @@ TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pu recompute_tempos (imaginary); } - recompute_meters (imaginary); if (check_solved (imaginary, false)) { return true; } @@ -2087,10 +2121,13 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t } } + /* it would make sense to bail out if there is no audio-locked meter, + however it may be desirable to move a music-locked meter by frame at some point. + */ TempoSection* meter_locked_tempo = 0; - for (Metrics::const_iterator i = imaginary.begin(); i != imaginary.end(); ++i) { + for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) { TempoSection* t; - if ((t = dynamic_cast (*i)) != 0) { + if ((t = dynamic_cast (*ii)) != 0) { if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) { meter_locked_tempo = t; break; @@ -2231,7 +2268,18 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t bool TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& when) { + /* disallow setting section to an existing meter's bbt */ + for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) { + MeterSection* m; + if ((m = dynamic_cast (*i)) != 0) { + if (m->bbt().bars == when.bars) { + return false; + } + } + } + MeterSection* prev_m = 0; + MeterSection* section_prev = 0; for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) { MeterSection* m; @@ -2239,25 +2287,32 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& pair b_bbt; double new_pulse = 0.0; - if (prev_m && m == section){ - const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar(); - const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse(); - - b_bbt = make_pair (beats + prev_m->beat(), when); + if (prev_m && m->bbt().bars > when.bars && !section_prev){ + section_prev = prev_m; + const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar(); + const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse(); + pair b_bbt = make_pair (beats + section_prev->beat(), when); section->set_beat (b_bbt); section->set_pulse (pulse); section->set_frame (frame_at_pulse_locked (imaginary, pulse)); - - prev_m = m; + prev_m = section; continue; - - } else if (m->bbt().bars == when.bars) { - return false; } if (m->position_lock_style() == AudioTime) { - if (m->movable()) { + TempoSection* meter_locked_tempo = 0; + for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) { + TempoSection* t; + if ((t = dynamic_cast (*ii)) != 0) { + if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) { + meter_locked_tempo = t; + break; + } + } + } + + if (prev_m) { const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar()); if (beats + prev_m->beat() != m->beat()) { @@ -2265,25 +2320,32 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& b_bbt = make_pair (beats + prev_m->beat() , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0)); new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor()); - } else { + } else if (m->movable()) { b_bbt = make_pair (m->beat(), m->bbt()); - new_pulse = pulse_at_frame_locked (imaginary, m->frame()); + new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor()); } } else { b_bbt = make_pair (0.0, BBT_Time (1, 1, 0)); } - + if (meter_locked_tempo) { + meter_locked_tempo->set_pulse (new_pulse); + recompute_tempos (imaginary); + } m->set_beat (b_bbt); m->set_pulse (new_pulse); } else { + /* MusicTime */ const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar()); - - b_bbt = make_pair (beats + prev_m->beat() - , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0)); - new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() - / prev_m->note_divisor()); - + if (beats + prev_m->beat() != m->beat()) { + /* tempo/ meter change caused a change in beat (bar). */ + b_bbt = make_pair (beats + prev_m->beat() + , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0)); + } else { + b_bbt = make_pair (beats + prev_m->beat() + , m->bbt()); + } + new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse(); m->set_beat (b_bbt); m->set_pulse (new_pulse); m->set_frame (frame_at_pulse_locked (imaginary, new_pulse)); @@ -2293,6 +2355,17 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& } } + if (!section_prev) { + + const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar(); + const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse(); + pair b_bbt = make_pair (beats + prev_m->beat(), when); + + section->set_beat (b_bbt); + section->set_pulse (pulse); + section->set_frame (frame_at_pulse_locked (imaginary, pulse)); + } + MetricSectionSorter cmp; imaginary.sort (cmp); if (section->position_lock_style() == AudioTime) { @@ -2303,6 +2376,7 @@ TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& } else { recompute_meters (imaginary); } + return true; } @@ -2520,6 +2594,7 @@ TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame) MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms); if (solve_map (future_map, copy, frame)) { solve_map (_metrics, ms, frame); + recompute_tempos (_metrics); } } @@ -2541,6 +2616,7 @@ TempoMap::gui_move_meter (MeterSection* ms, const Timecode::BBT_Time& bbt) MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms); if (solve_map (future_map, copy, bbt)) { solve_map (_metrics, ms, bbt); + recompute_tempos (_metrics); } } @@ -2592,6 +2668,7 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame) /* disabled for now due to faked tempo locked to meter pulse */ return; } + { Glib::Threads::RWLock::WriterLock lm (lock); ts = const_cast(&tempo_section_at_locked (_metrics, ms->frame() - 1)); @@ -2603,7 +2680,7 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame) const frameoffset_t fr_off = frame - ms->frame(); double new_bpm = 0.0; - if (prev_t) { + if (prev_t && prev_t->pulse() > 0.0) { prev_to_prev_t = const_cast(&tempo_section_at_locked (future_map, prev_t->frame() - 1)); } @@ -2709,7 +2786,6 @@ TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame) } MetricPositionChanged (); // Emit Signal - return; } framecnt_t