0001-implement-time-stretch-from-left-of-region.patch (11,146 bytes)
From 793379bd53366bcfbab1506dd36ae2c47288e76e Mon Sep 17 00:00:00 2001
From: Colin Fletcher <colin.m.fletcher@googlemail.com>
Date: Thu, 8 Jun 2023 22:33:14 +0100
Subject: [PATCH] implement time-stretch from left of region
Make click & drag in the left-hand half of a region with the Timestretch
tool stretch the region on its left, leaving the end position of the new
time-stretched region in the same place as the end of the original.
---
gtk2_ardour/editor.h | 6 +++---
gtk2_ardour/editor_drag.cc | 36 ++++++++++++++++++++++++++---------
gtk2_ardour/editor_drag.h | 2 ++
gtk2_ardour/editor_timefx.cc | 30 +++++++++++++++++++----------
gtk2_ardour/time_fx_dialog.cc | 3 ++-
gtk2_ardour/time_fx_dialog.h | 3 ++-
6 files changed, 56 insertions(+), 24 deletions(-)
diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h
index 8cba3635b9..0e02fefd41 100644
--- a/gtk2_ardour/editor.h
+++ b/gtk2_ardour/editor.h
@@ -2251,9 +2251,9 @@ private:
TimeFXDialog* current_timefx;
static void* timefx_thread (void* arg);
- void do_timefx ();
+ void do_timefx (bool fixed_end);
- int time_stretch (RegionSelection&, Temporal::ratio_t const & fraction);
+ int time_stretch (RegionSelection&, Temporal::ratio_t const & fraction, bool fixed_end);
int pitch_shift (RegionSelection&, float cents);
void pitch_shift_region ();
@@ -2486,7 +2486,7 @@ private:
void set_show_touched_automation (bool);
bool _show_touched_automation;
- int time_fx (ARDOUR::RegionList&, Temporal::ratio_t ratio, bool pitching);
+ int time_fx (ARDOUR::RegionList&, Temporal::ratio_t ratio, bool pitching, bool fixed_end);
void note_edit_done (int, EditNoteDialog*);
void toggle_sound_midi_notes ();
diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc
index 9ad26b58a5..4f19506275 100644
--- a/gtk2_ardour/editor_drag.cc
+++ b/gtk2_ardour/editor_drag.cc
@@ -5302,7 +5302,9 @@ TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
timepos_t where (_primary->region ()->position ());
setup_snap_delta (_primary->region ()->position ());
- show_verbose_cursor_duration (where, adjusted_current_time (event), 0);
+ timepos_t clicked_pos = adjusted_current_time (event);
+ show_verbose_cursor_duration (where, clicked_pos, 0);
+ _dragging_start = clicked_pos < _primary->region ()->position () + _primary->region ()->length ().scale(ratio_t(1, 2));
}
void
@@ -5319,8 +5321,15 @@ TimeFXDrag::motion (GdkEvent* event, bool)
_editor->snap_to_with_modifier (pf, event);
pf.shift_earlier (snap_delta (event->button.state));
- if (pf > rv->region ()->position ()) {
- rv->get_time_axis_view ().show_timestretch (rv->region ()->position (), pf, layers, layer);
+ if (_dragging_start) {
+ if (pf < rv->region ()->end ()) {
+ rv->get_time_axis_view ().show_timestretch (pf, rv->region ()->end (), layers, layer);
+
+ }
+ } else {
+ if (pf > rv->region ()->position ()) {
+ rv->get_time_axis_view ().show_timestretch (rv->region ()->position (), pf, layers, layer);
+ }
}
show_verbose_cursor_duration (_primary->region ()->position (), pf);
@@ -5342,7 +5351,7 @@ TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
if (!movement_occurred) {
_primary->get_time_axis_view ().hide_timestretch ();
- if (_editor->time_stretch (_editor->get_selection ().regions, ratio_t (1, 1)) == -1) {
+ if (_editor->time_stretch (_editor->get_selection ().regions, ratio_t (1, 1), false) == -1) {
error << _("An error occurred while executing time stretch operation") << endmsg;
}
return;
@@ -5355,13 +5364,22 @@ TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
_primary->get_time_axis_view ().hide_timestretch ();
timepos_t adjusted_pos = adjusted_current_time (event);
+ timecnt_t newlen;
- if (adjusted_pos < _primary->region ()->position ()) {
- /* backwards drag of the left edge - not usable */
- return;
+ if (_dragging_start) {
+ if (adjusted_pos > _primary->region ()->end ()) {
+ /* forwards drag of the right edge - not usable */
+ return;
+ }
+ newlen = _primary->region ()->end ().distance (adjusted_pos);
+ } else {
+ if (adjusted_pos < _primary->region ()->position ()) {
+ /* backwards drag of the left edge - not usable */
+ return;
+ }
+ newlen = _primary->region ()->position ().distance (adjusted_pos);
}
- 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 ());
@@ -5383,7 +5401,7 @@ TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
selection.
*/
- if (_editor->time_stretch (_editor->get_selection ().regions, ratio) == -1) {
+ if (_editor->time_stretch (_editor->get_selection ().regions, ratio, _dragging_start) == -1) {
error << _("An error occurred while executing time stretch operation") << endmsg;
}
}
diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h
index 4729886098..ce84b54427 100644
--- a/gtk2_ardour/editor_drag.h
+++ b/gtk2_ardour/editor_drag.h
@@ -1335,6 +1335,8 @@ public:
void motion (GdkEvent *, bool);
void finished (GdkEvent *, bool);
void aborted (bool);
+private:
+ bool _dragging_start;
};
/** Scrub drag in audition mode */
diff --git a/gtk2_ardour/editor_timefx.cc b/gtk2_ardour/editor_timefx.cc
index cd529725e9..c78328ace3 100644
--- a/gtk2_ardour/editor_timefx.cc
+++ b/gtk2_ardour/editor_timefx.cc
@@ -66,7 +66,7 @@ using namespace Gtkmm2ext;
/** @return -1 in case of error, 1 if operation was cancelled by the user, 0 if everything went ok */
int
-Editor::time_stretch (RegionSelection& regions, Temporal::ratio_t const & ratio)
+Editor::time_stretch (RegionSelection& regions, Temporal::ratio_t const & ratio, bool fixed_end)
{
RegionList audio;
RegionList midi;
@@ -81,7 +81,7 @@ Editor::time_stretch (RegionSelection& regions, Temporal::ratio_t const & ratio)
}
}
- int aret = time_fx (audio, ratio, false);
+ int aret = time_fx (audio, ratio, false, fixed_end);
if (aret < 0) {
abort_reversible_command ();
return aret;
@@ -111,8 +111,14 @@ Editor::time_stretch (RegionSelection& regions, Temporal::ratio_t const & ratio)
MidiStretch stretch (*_session, request);
stretch.run (*i);
- playlist->replace_region (regions.front()->region(), stretch.results[0],
- regions.front()->region()->position());
+ timepos_t newpos;
+
+ if (fixed_end)
+ newpos = regions.front()->region()->end().earlier(stretch.results[0]->length());
+ else
+ newpos = regions.front()->region()->position();
+
+ playlist->replace_region (regions.front()->region(), stretch.results[0], newpos);
midi_playlists_affected.insert (playlist);
}
@@ -144,7 +150,7 @@ Editor::pitch_shift (RegionSelection& regions, float fraction)
begin_reversible_command (_("pitch shift"));
- int ret = time_fx (rl, fraction, true);
+ int ret = time_fx (rl, fraction, true, false);
if (ret > 0) {
commit_reversible_command ();
@@ -159,7 +165,7 @@ Editor::pitch_shift (RegionSelection& regions, float fraction)
* @param pitching true to pitch shift, false to time stretch.
* @return -1 in case of error, otherwise number of regions processed */
int
-Editor::time_fx (RegionList& regions, Temporal::ratio_t ratio, bool pitching)
+Editor::time_fx (RegionList& regions, Temporal::ratio_t ratio, bool pitching, bool fixed_end)
{
delete current_timefx;
@@ -172,7 +178,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, ratio, pos);
+ current_timefx = new TimeFXDialog (*this, pitching, oldlen, newlen, ratio, pos, fixed_end);
current_timefx->regions = regions;
switch (current_timefx->run ()) {
@@ -355,7 +361,7 @@ Editor::time_fx (RegionList& regions, Temporal::ratio_t ratio, bool pitching)
}
void
-Editor::do_timefx ()
+Editor::do_timefx (bool fixed_end)
{
typedef std::map<std::shared_ptr<Region>, std::shared_ptr<Region> > ResultMap;
ResultMap results;
@@ -426,7 +432,11 @@ Editor::do_timefx ()
std::shared_ptr<Playlist> playlist = region->playlist();
playlist->clear_changes ();
- playlist->replace_region (region, new_region, region->position());
+ if (fixed_end)
+ playlist->replace_region (region, new_region, region->end ().earlier(new_region->length ()));
+ else
+ playlist->replace_region (region, new_region, region->position());
+
PBD::StatefulDiffCommand* cmd = new StatefulDiffCommand (playlist);
_session->add_command (cmd);
if (!cmd->empty ()) {
@@ -449,7 +459,7 @@ Editor::timefx_thread (void *arg)
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
- tsd->editor.do_timefx ();
+ tsd->editor.do_timefx (tsd->fixed_end);
/* GACK! HACK! sleep for a bit so that our request buffer for the GUI
event loop doesn't die before any changes we made are processed
diff --git a/gtk2_ardour/time_fx_dialog.cc b/gtk2_ardour/time_fx_dialog.cc
index 7c58bd2f5f..e3e702a3e9 100644
--- a/gtk2_ardour/time_fx_dialog.cc
+++ b/gtk2_ardour/time_fx_dialog.cc
@@ -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, Temporal::ratio_t const & ratio, 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, bool fixed_end)
: ArdourDialog (X_("time fx dialog"))
, editor (e)
, pitching (pitch)
@@ -82,6 +82,7 @@ TimeFXDialog::TimeFXDialog (Editor& e, bool pitch, timecnt_t const & oldlen, tim
set_skip_taskbar_hint (true);
set_resizable (false);
set_name (N_("TimeFXDialog"));
+ this->fixed_end = fixed_end;
if (pitching) {
set_title (_("Pitch Shift Audio"));
diff --git a/gtk2_ardour/time_fx_dialog.h b/gtk2_ardour/time_fx_dialog.h
index f4a80bd793..d1665e776d 100644
--- a/gtk2_ardour/time_fx_dialog.h
+++ b/gtk2_ardour/time_fx_dialog.h
@@ -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::ratio_t const &, 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, bool fixed_end);
ARDOUR::TimeFXRequest request;
Editor& editor;
@@ -65,6 +65,7 @@ public:
Gtk::Button* action_button;
Gtk::VBox packer;
int status;
+ bool fixed_end;
sigc::connection first_cancel;
sigc::connection first_delete;
--
2.30.2