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.
This commit is contained in:
parent
96eb7652c9
commit
72850d456f
@ -2251,9 +2251,9 @@ private:
|
|||||||
|
|
||||||
TimeFXDialog* current_timefx;
|
TimeFXDialog* current_timefx;
|
||||||
static void* timefx_thread (void* arg);
|
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);
|
int pitch_shift (RegionSelection&, float cents);
|
||||||
void pitch_shift_region ();
|
void pitch_shift_region ();
|
||||||
|
|
||||||
@ -2486,7 +2486,7 @@ private:
|
|||||||
void set_show_touched_automation (bool);
|
void set_show_touched_automation (bool);
|
||||||
bool _show_touched_automation;
|
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 note_edit_done (int, EditNoteDialog*);
|
||||||
void toggle_sound_midi_notes ();
|
void toggle_sound_midi_notes ();
|
||||||
|
|
||||||
|
@ -5302,7 +5302,9 @@ TimeFXDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
|
|||||||
timepos_t where (_primary->region ()->position ());
|
timepos_t where (_primary->region ()->position ());
|
||||||
setup_snap_delta (_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
|
void
|
||||||
@ -5319,9 +5321,16 @@ TimeFXDrag::motion (GdkEvent* event, bool)
|
|||||||
_editor->snap_to_with_modifier (pf, event);
|
_editor->snap_to_with_modifier (pf, event);
|
||||||
pf.shift_earlier (snap_delta (event->button.state));
|
pf.shift_earlier (snap_delta (event->button.state));
|
||||||
|
|
||||||
|
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 ()) {
|
if (pf > rv->region ()->position ()) {
|
||||||
rv->get_time_axis_view ().show_timestretch (rv->region ()->position (), pf, layers, layer);
|
rv->get_time_axis_view ().show_timestretch (rv->region ()->position (), pf, layers, layer);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
show_verbose_cursor_duration (_primary->region ()->position (), pf);
|
show_verbose_cursor_duration (_primary->region ()->position (), pf);
|
||||||
}
|
}
|
||||||
@ -5342,7 +5351,7 @@ TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
|
|||||||
if (!movement_occurred) {
|
if (!movement_occurred) {
|
||||||
_primary->get_time_axis_view ().hide_timestretch ();
|
_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;
|
error << _("An error occurred while executing time stretch operation") << endmsg;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -5355,13 +5364,22 @@ TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
|
|||||||
_primary->get_time_axis_view ().hide_timestretch ();
|
_primary->get_time_axis_view ().hide_timestretch ();
|
||||||
|
|
||||||
timepos_t adjusted_pos = adjusted_current_time (event);
|
timepos_t adjusted_pos = adjusted_current_time (event);
|
||||||
|
timecnt_t newlen;
|
||||||
|
|
||||||
|
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 ()) {
|
if (adjusted_pos < _primary->region ()->position ()) {
|
||||||
/* backwards drag of the left edge - not usable */
|
/* backwards drag of the left edge - not usable */
|
||||||
return;
|
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) {
|
if (_primary->region ()->length ().time_domain () == Temporal::BeatTime) {
|
||||||
ratio = ratio_t (newlen.ticks (), _primary->region ()->length ().ticks ());
|
ratio = ratio_t (newlen.ticks (), _primary->region ()->length ().ticks ());
|
||||||
@ -5383,7 +5401,7 @@ TimeFXDrag::finished (GdkEvent* event, bool movement_occurred)
|
|||||||
selection.
|
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;
|
error << _("An error occurred while executing time stretch operation") << endmsg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1335,6 +1335,8 @@ public:
|
|||||||
void motion (GdkEvent *, bool);
|
void motion (GdkEvent *, bool);
|
||||||
void finished (GdkEvent *, bool);
|
void finished (GdkEvent *, bool);
|
||||||
void aborted (bool);
|
void aborted (bool);
|
||||||
|
private:
|
||||||
|
bool _dragging_start;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Scrub drag in audition mode */
|
/** Scrub drag in audition mode */
|
||||||
|
@ -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 */
|
/** @return -1 in case of error, 1 if operation was cancelled by the user, 0 if everything went ok */
|
||||||
int
|
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 audio;
|
||||||
RegionList midi;
|
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) {
|
if (aret < 0) {
|
||||||
abort_reversible_command ();
|
abort_reversible_command ();
|
||||||
return aret;
|
return aret;
|
||||||
@ -111,8 +111,14 @@ Editor::time_stretch (RegionSelection& regions, Temporal::ratio_t const & ratio)
|
|||||||
MidiStretch stretch (*_session, request);
|
MidiStretch stretch (*_session, request);
|
||||||
stretch.run (*i);
|
stretch.run (*i);
|
||||||
|
|
||||||
playlist->replace_region (regions.front()->region(), stretch.results[0],
|
timepos_t newpos;
|
||||||
regions.front()->region()->position());
|
|
||||||
|
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);
|
midi_playlists_affected.insert (playlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +150,7 @@ Editor::pitch_shift (RegionSelection& regions, float fraction)
|
|||||||
|
|
||||||
begin_reversible_command (_("pitch shift"));
|
begin_reversible_command (_("pitch shift"));
|
||||||
|
|
||||||
int ret = time_fx (rl, fraction, true);
|
int ret = time_fx (rl, fraction, true, false);
|
||||||
|
|
||||||
if (ret > 0) {
|
if (ret > 0) {
|
||||||
commit_reversible_command ();
|
commit_reversible_command ();
|
||||||
@ -159,7 +165,7 @@ Editor::pitch_shift (RegionSelection& regions, float fraction)
|
|||||||
* @param pitching true to pitch shift, false to time stretch.
|
* @param pitching true to pitch shift, false to time stretch.
|
||||||
* @return -1 in case of error, otherwise number of regions processed */
|
* @return -1 in case of error, otherwise number of regions processed */
|
||||||
int
|
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;
|
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 timecnt_t newlen = regions.front()->length().scale (ratio);
|
||||||
const timepos_t pos = regions.front()->position ();
|
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;
|
current_timefx->regions = regions;
|
||||||
|
|
||||||
switch (current_timefx->run ()) {
|
switch (current_timefx->run ()) {
|
||||||
@ -355,7 +361,7 @@ Editor::time_fx (RegionList& regions, Temporal::ratio_t ratio, bool pitching)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Editor::do_timefx ()
|
Editor::do_timefx (bool fixed_end)
|
||||||
{
|
{
|
||||||
typedef std::map<std::shared_ptr<Region>, std::shared_ptr<Region> > ResultMap;
|
typedef std::map<std::shared_ptr<Region>, std::shared_ptr<Region> > ResultMap;
|
||||||
ResultMap results;
|
ResultMap results;
|
||||||
@ -426,7 +432,11 @@ Editor::do_timefx ()
|
|||||||
std::shared_ptr<Playlist> playlist = region->playlist();
|
std::shared_ptr<Playlist> playlist = region->playlist();
|
||||||
|
|
||||||
playlist->clear_changes ();
|
playlist->clear_changes ();
|
||||||
|
if (fixed_end)
|
||||||
|
playlist->replace_region (region, new_region, region->end ().earlier(new_region->length ()));
|
||||||
|
else
|
||||||
playlist->replace_region (region, new_region, region->position());
|
playlist->replace_region (region, new_region, region->position());
|
||||||
|
|
||||||
PBD::StatefulDiffCommand* cmd = new StatefulDiffCommand (playlist);
|
PBD::StatefulDiffCommand* cmd = new StatefulDiffCommand (playlist);
|
||||||
_session->add_command (cmd);
|
_session->add_command (cmd);
|
||||||
if (!cmd->empty ()) {
|
if (!cmd->empty ()) {
|
||||||
@ -449,7 +459,7 @@ Editor::timefx_thread (void *arg)
|
|||||||
|
|
||||||
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
|
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
|
/* 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
|
event loop doesn't die before any changes we made are processed
|
||||||
|
@ -55,7 +55,7 @@ using namespace PBD;
|
|||||||
using namespace Gtk;
|
using namespace Gtk;
|
||||||
using namespace Gtkmm2ext;
|
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"))
|
: ArdourDialog (X_("time fx dialog"))
|
||||||
, editor (e)
|
, editor (e)
|
||||||
, pitching (pitch)
|
, pitching (pitch)
|
||||||
@ -64,6 +64,7 @@ TimeFXDialog::TimeFXDialog (Editor& e, bool pitch, timecnt_t const & oldlen, tim
|
|||||||
, stretch_opts_label (_("Contents"))
|
, stretch_opts_label (_("Contents"))
|
||||||
, precise_button (_("Minimize time distortion"))
|
, precise_button (_("Minimize time distortion"))
|
||||||
, preserve_formants_button(_("Preserve Formants"))
|
, preserve_formants_button(_("Preserve Formants"))
|
||||||
|
, fixed_end (fixed_end)
|
||||||
, original_length (oldlen)
|
, original_length (oldlen)
|
||||||
, pitch_octave_adjustment (0.0, -4.0, 4.0, 1, 2.0)
|
, pitch_octave_adjustment (0.0, -4.0, 4.0, 1, 2.0)
|
||||||
, pitch_semitone_adjustment (0.0, -12.0, 12.0, 1.0, 4.0)
|
, pitch_semitone_adjustment (0.0, -12.0, 12.0, 1.0, 4.0)
|
||||||
|
@ -42,7 +42,7 @@ class TimeFXDialog : public ArdourDialog, public ProgressReporter
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/* We need a position so that BBT mode in the clock can function */
|
/* 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;
|
ARDOUR::TimeFXRequest request;
|
||||||
Editor& editor;
|
Editor& editor;
|
||||||
@ -65,6 +65,7 @@ public:
|
|||||||
Gtk::Button* action_button;
|
Gtk::Button* action_button;
|
||||||
Gtk::VBox packer;
|
Gtk::VBox packer;
|
||||||
int status;
|
int status;
|
||||||
|
bool fixed_end;
|
||||||
|
|
||||||
sigc::connection first_cancel;
|
sigc::connection first_cancel;
|
||||||
sigc::connection first_delete;
|
sigc::connection first_delete;
|
||||||
|
Loading…
Reference in New Issue
Block a user