timestretching: fix inaccuracies in generated length

The design ignored the ratio computed by the drag interaction, and relied on getting the stretch
ratio from the dialog. This truncated the actual ratio, leading to (relatively) small errors
in the length of the generated region.

Now, if the ratio provided by the drag is not (1/1) (i.e. a single click while in timefx mode)
then the percentage stretch spinner is marked insensitive and the stretch ratio is taken from
the given ratio. For single clicks, the user can still adjust the percentage as they wish
This commit is contained in:
Paul Davis 2022-05-31 12:36:49 -06:00
parent 2d24bcdf4a
commit b806ccf373
4 changed files with 63 additions and 41 deletions

View File

@ -5517,48 +5517,58 @@ TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
parameters for the timestretch.
*/
ratio_t ratio (1, 1);
if (movement_occurred) {
motion (event, false);
if (_editor->get_selection().regions.empty()) {
_primary->get_time_axis_view().hide_timestretch ();
timepos_t adjusted_pos = adjusted_current_time (event);
if (adjusted_pos < _primary->region()->position()) {
/* backwards drag of the left edge - not usable */
return;
}
timecnt_t newlen = _primary->region()->position().distance (adjusted_pos);
if (_primary->region()->length().time_domain() == Temporal::BeatTime) {
ratio = ratio_t (newlen.ticks(), _primary->region()->length().ticks());
} else {
ratio = ratio_t (newlen.samples(), _primary->region()->length().samples());
}
#ifndef USE_RUBBERBAND
// Soundtouch uses fraction / 100 instead of normal (/ 1)
#warning NUTEMPO timefx request now uses a rational type so this needs revisiting
if (_primary->region()->data_type() == DataType::AUDIO) {
ratio = ((newlen - _primary->region()->length()) / newlen) * 100;
}
#endif
return;
}
if (!_editor->get_selection().regions.empty()) {
/* primary will already be included in the selection, and edit
group shared editing will propagate selection across
equivalent regions, so just use the current region
selection.
*/
if (!movement_occurred) {
_primary->get_time_axis_view().hide_timestretch ();
if (_editor->time_stretch (_editor->get_selection().regions, ratio) == -1) {
if (_editor->time_stretch (_editor->get_selection().regions, ratio_t (1, 1)) == -1) {
error << _("An error occurred while executing time stretch operation") << endmsg;
}
return;
}
ratio_t ratio (1, 1);
motion (event, false);
_primary->get_time_axis_view().hide_timestretch ();
timepos_t adjusted_pos = adjusted_current_time (event);
if (adjusted_pos < _primary->region()->position()) {
/* backwards drag of the left edge - not usable */
return;
}
timecnt_t newlen = _primary->region()->position().distance (adjusted_pos);
if (_primary->region()->length().time_domain() == Temporal::BeatTime) {
ratio = ratio_t (newlen.ticks(), _primary->region()->length().ticks());
} else {
ratio = ratio_t (newlen.samples(), _primary->region()->length().samples());
}
#ifndef USE_RUBBERBAND
// Soundtouch uses fraction / 100 instead of normal (/ 1)
#warning NUTEMPO timefx request now uses a rational type so this needs revisiting
if (_primary->region()->data_type() == DataType::AUDIO) {
ratio = ((newlen - _primary->region()->length()) / newlen) * 100;
}
#endif
/* primary will already be included in the selection, and edit
group shared editing will propagate selection across
equivalent regions, so just use the current region
selection.
*/
if (_editor->time_stretch (_editor->get_selection().regions, ratio) == -1) {
error << _("An error occurred while executing time stretch operation") << endmsg;
}
}

View File

@ -172,7 +172,7 @@ Editor::time_fx (RegionList& regions, Temporal::ratio_t ratio, bool pitching)
const timecnt_t newlen = regions.front()->length().scale (ratio);
const timepos_t pos = regions.front()->position ();
current_timefx = new TimeFXDialog (*this, pitching, oldlen, newlen, pos);
current_timefx = new TimeFXDialog (*this, pitching, oldlen, newlen, ratio, pos);
current_timefx->regions = regions;
switch (current_timefx->run ()) {

View File

@ -55,7 +55,7 @@ using namespace PBD;
using namespace Gtk;
using namespace Gtkmm2ext;
TimeFXDialog::TimeFXDialog (Editor& e, bool pitch, timecnt_t const & oldlen, timecnt_t const & new_length, timepos_t const & position)
TimeFXDialog::TimeFXDialog (Editor& e, bool pitch, timecnt_t const & oldlen, timecnt_t const & new_length, Temporal::ratio_t const & ratio, timepos_t const & position)
: ArdourDialog (X_("time fx dialog"))
, editor (e)
, pitching (pitch)
@ -71,6 +71,7 @@ TimeFXDialog::TimeFXDialog (Editor& e, bool pitch, timecnt_t const & oldlen, tim
, pitch_octave_spinner (pitch_octave_adjustment)
, pitch_semitone_spinner (pitch_semitone_adjustment)
, pitch_cent_spinner (pitch_cent_adjustment)
, duration_ratio (ratio)
, duration_adjustment (100.0, -1000.0, 1000.0, 1.0, 10.0)
, duration_clock (0)
, ignore_adjustment_change (false)
@ -105,6 +106,8 @@ TimeFXDialog::TimeFXDialog (Editor& e, bool pitch, timecnt_t const & oldlen, tim
upper_button_box.pack_start (*l, false, false);
/* if the ratio is already set, do not allow adjustment */
if (pitching) {
Table* table = manage (new Table (4, 3, false));
table->set_row_spacings (6);
@ -158,11 +161,15 @@ TimeFXDialog::TimeFXDialog (Editor& e, bool pitch, timecnt_t const & oldlen, tim
table->attach (*clock_align, 1, 2, row, row+1, Gtk::AttachOptions (Gtk::EXPAND|Gtk::FILL), Gtk::FILL, 0, 0);
row++;
const double fract = (new_length / original_length).to_double();
/* note the *100.0 to convert fract into a percentage */
duration_adjustment.set_value (fract*100.0);
duration_adjustment.set_value (duration_ratio.to_double() * 100.0);
Gtk::SpinButton* spinner = manage (new Gtk::SpinButton (duration_adjustment, 1.0, 3));
if (duration_ratio != Temporal::ratio_t (1, 1)) {
spinner->set_sensitive (false);
}
l = manage (new Gtk::Label (_("Percent")));
table->attach (*l, 0, 1, row, row+1, Gtk::FILL, Gtk::FILL, 0, 0);
table->attach (*spinner, 1, 2, row, row+1, Gtk::FILL, Gtk::FILL, 0, 0);
@ -266,6 +273,10 @@ TimeFXDialog::get_time_fraction () const
return Temporal::ratio_t (1, 1);
}
if (duration_ratio != Temporal::ratio_t (1, 1)) {
return duration_ratio;
}
return Temporal::ratio_t (duration_adjustment.get_value(), 100);
}

View File

@ -42,7 +42,7 @@ class TimeFXDialog : public ArdourDialog, public ProgressReporter
{
public:
/* We need a position so that BBT mode in the clock can function */
TimeFXDialog (Editor& e, bool for_pitch, Temporal::timecnt_t const & old_length, Temporal::timecnt_t const & new_length, Temporal::timepos_t const & position);
TimeFXDialog (Editor& e, bool for_pitch, Temporal::timecnt_t const & old_length, Temporal::timecnt_t const & new_length, Temporal::ratio_t const &, Temporal::timepos_t const & position);
ARDOUR::TimeFXRequest request;
Editor& editor;
@ -93,6 +93,7 @@ private:
Gtk::SpinButton pitch_octave_spinner;
Gtk::SpinButton pitch_semitone_spinner;
Gtk::SpinButton pitch_cent_spinner;
Temporal::ratio_t duration_ratio;
Gtk::Adjustment duration_adjustment;
AudioClock* duration_clock;
bool ignore_adjustment_change;