From 45de3694cc91e66912f4d86ba025677da029cccb Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 12 May 2022 12:14:45 -0600 Subject: [PATCH] temporal: add reimplemented version of 6.x's TempoMap::gui_stretch_tempo() --- libs/temporal/tempo.cc | 86 ++++++++++++++++++++++++++++++++++ libs/temporal/temporal/tempo.h | 4 ++ 2 files changed, 90 insertions(+) diff --git a/libs/temporal/tempo.cc b/libs/temporal/tempo.cc index 3e48d2f08e..e9e3a67cdc 100644 --- a/libs/temporal/tempo.cc +++ b/libs/temporal/tempo.cc @@ -3028,6 +3028,92 @@ TempoMap::set_ramped (TempoPoint & tp, bool yn) reset_starting_at (tp.sclock() + 1); } + +void +TempoMap::stretch_tempo (TempoPoint* ts, const samplepos_t sample, const samplepos_t end_sample, Beats const & start_qnote, Beats const & end_qnote) +{ + /* + Ts (future prev_t) Tnext + | | + | [drag^] | + |----------|---------- + e_f qn_beats(sample) + */ + + if (!ts) { + return; + } + + superclock_t start_sclock = samples_to_superclock (sample, TEMPORAL_SAMPLE_RATE); + superclock_t end_sclock = samples_to_superclock (end_sample, TEMPORAL_SAMPLE_RATE); + + /* minimum allowed measurement distance in samples */ + const superclock_t min_delta_sclock = samples_to_superclock (2, TEMPORAL_SAMPLE_RATE); + double new_bpm; + + if (ts->clamped()) { + + /* this tempo point is required to start using the same bpm + * that the previous tempo ended with. + */ + + TempoPoint* next_t = const_cast (next_tempo (*ts)); + TempoPoint* prev_to_ts = const_cast (previous_tempo (*ts)); + assert (prev_to_ts); + /* the change in samples is the result of changing the slope of at most 2 previous tempo sections. + * constant to constant is straightforward, as the tempo prev to ts has constant slope. + */ + double contribution = 0.0; + if (next_t && prev_to_ts->ramped()) { + const DoubleableBeats delta_tp = ts->beats() - prev_to_ts->beats(); + const DoubleableBeats delta_np = next_t->beats() - prev_to_ts->beats(); + contribution = delta_tp.to_double() / delta_np.to_double(); + } + samplepos_t const fr_off = end_sclock - start_sclock; + sampleoffset_t const ts_sample_contribution = fr_off - (contribution * (double) fr_off); + + if (start_sclock > prev_to_ts->sclock() + min_delta_sclock && (start_sclock + ts_sample_contribution) > prev_to_ts->sclock() + min_delta_sclock) { + DoubleableBeats delta_sp = start_qnote - prev_to_ts->beats(); + DoubleableBeats delta_ep = end_qnote - prev_to_ts->beats(); + new_bpm = ts->note_types_per_minute() * (delta_sp.to_double() / delta_ep.to_double()); + } else { + new_bpm = ts->note_types_per_minute(); + } + + } else { + + /* ts is free to have it's bpm changed to any value (within limits) */ + + if (start_sclock > ts->sclock() + min_delta_sclock && end_sclock > ts->sclock() + min_delta_sclock) { + new_bpm = ts->note_types_per_minute() * ((start_sclock - ts->sclock()) / (double) (end_sclock - ts->sclock())); + } else { + new_bpm = ts->note_types_per_minute(); + } + + new_bpm = std::min (new_bpm, 1000.0); + } + /* don't clamp and proceed here. + testing has revealed that this can go negative, + which is an entirely different thing to just being too low. + */ + + if (new_bpm < 0.5) { + return; + } + + ts->set_note_types_per_minute (new_bpm); + + if (ts->clamped()) { + TempoPoint* prev = 0; + if ((prev = const_cast (previous_tempo (*ts))) != 0) { + prev->set_end_note_types_per_minute (ts->note_types_per_minute()); + } + } + + reset_starting_at (ts->sclock() + 1); +} + + void TempoMap::twist_tempi (TempoPoint* ts, timepos_t const & start, timepos_t const & end) { diff --git a/libs/temporal/temporal/tempo.h b/libs/temporal/temporal/tempo.h index a96565e6c2..3d62b4e237 100644 --- a/libs/temporal/temporal/tempo.h +++ b/libs/temporal/temporal/tempo.h @@ -216,7 +216,9 @@ class LIBTEMPORAL_API Tempo : public Rampable { double quarter_notes_per_minute() const { return (superclock_ticks_per_second() * 60.0 * 4.0) / (_note_type * _superclocks_per_note_type); } double samples_per_note_type(samplecnt_t sr) const { return superclock_to_samples (superclocks_per_note_type (), sr); } double samples_per_quarter_note(samplecnt_t sr) const { return superclock_to_samples (superclocks_per_quarter_note(), sr); } + void set_note_types_per_minute (double npm) { _superclocks_per_note_type = double_npm_to_scpn (npm); } + void set_end_note_types_per_minute (double npm) { _end_superclocks_per_note_type = double_npm_to_scpn (npm); } int note_type () const { return _note_type; } Beats note_type_as_beats () const { return Beats (0, (1920 * 4) / _note_type); } @@ -734,6 +736,8 @@ class /*LIBTEMPORAL_API*/ TempoMap : public PBD::StatefulDestructible LIBTEMPORAL_API int set_state (XMLNode const&, int version); LIBTEMPORAL_API void twist_tempi (TempoPoint* ts, timepos_t const & start, timepos_t const & end); + LIBTEMPORAL_API void stretch_tempo (TempoPoint* ts, const samplepos_t sample, const samplepos_t end_sample, Beats const & start_qnote, Beats const & end_qnote); + LIBTEMPORAL_API void stretch_tempo_end (TempoPoint* ts, const samplepos_t sample, const samplepos_t end_sample); /* END OF MODIFYING METHODS */