first steps in providing more precise control over timestretching

This commit is contained in:
Paul Davis 2016-02-21 16:28:44 -05:00
parent 276b820923
commit 60055a0d6a
4 changed files with 173 additions and 111 deletions

View file

@ -430,6 +430,10 @@
<ColorAlias name="stereo panner outline" alias="color 70"/> <ColorAlias name="stereo panner outline" alias="color 70"/>
<ColorAlias name="stereo panner rule" alias="color 70"/> <ColorAlias name="stereo panner rule" alias="color 70"/>
<ColorAlias name="stereo panner text" alias="color 4"/> <ColorAlias name="stereo panner text" alias="color 4"/>
<ColorAlias name="stretch clock: background" alias="color 67"/>
<ColorAlias name="stretch clock: cursor" alias="color 8"/>
<ColorAlias name="stretch clock: edited text" alias="color 8"/>
<ColorAlias name="stretch clock: text" alias="color 91"/>
<ColorAlias name="sync mark" alias="color 75"/> <ColorAlias name="sync mark" alias="color 75"/>
<ColorAlias name="tempo bar" alias="color 46"/> <ColorAlias name="tempo bar" alias="color 46"/>
<ColorAlias name="tempo marker" alias="color 19"/> <ColorAlias name="tempo marker" alias="color 19"/>

View file

@ -29,6 +29,13 @@
#include "pbd/memento_command.h" #include "pbd/memento_command.h"
#include "pbd/stateful_diff_command.h" #include "pbd/stateful_diff_command.h"
#include "ardour/audioregion.h"
#include "ardour/midi_stretch.h"
#include "ardour/pitch.h"
#include "ardour/region.h"
#include "ardour/session.h"
#include "ardour/stretch.h"
#include <gtkmm2ext/utils.h> #include <gtkmm2ext/utils.h>
#include "audio_region_view.h" #include "audio_region_view.h"
@ -37,13 +44,6 @@
#include "region_selection.h" #include "region_selection.h"
#include "time_fx_dialog.h" #include "time_fx_dialog.h"
#include "ardour/audioregion.h"
#include "ardour/midi_stretch.h"
#include "ardour/pitch.h"
#include "ardour/region.h"
#include "ardour/session.h"
#include "ardour/stretch.h"
#ifdef USE_RUBBERBAND #ifdef USE_RUBBERBAND
#include <rubberband/RubberBandStretcher.h> #include <rubberband/RubberBandStretcher.h>
using namespace RubberBand; using namespace RubberBand;
@ -146,22 +146,18 @@ Editor::pitch_shift (RegionSelection& regions, float fraction)
int int
Editor::time_fx (RegionList& regions, float val, bool pitching) Editor::time_fx (RegionList& regions, float val, bool pitching)
{ {
delete current_timefx; if (regions.empty()) {
current_timefx = new TimeFXDialog (*this, pitching);
current_timefx->regions = regions;
/* See if we have any audio regions on our list */
RegionList::iterator i = regions.begin ();
while (i != regions.end() && boost::dynamic_pointer_cast<AudioRegion> (*i) == 0) {
++i;
}
if (i == regions.end ()) {
/* No audio regions; we can just do the timefx without a dialogue */
do_timefx ();
return 0; return 0;
} }
const framecnt_t oldlen = (framecnt_t) (regions.front()->length());
const framecnt_t newlen = (framecnt_t) (regions.front()->length() * val);
const framecnt_t pos = regions.front()->position ();
delete current_timefx;
current_timefx = new TimeFXDialog (*this, pitching, oldlen, newlen, pos);
current_timefx->regions = regions;
switch (current_timefx->run ()) { switch (current_timefx->run ()) {
case RESPONSE_ACCEPT: case RESPONSE_ACCEPT:
break; break;
@ -171,34 +167,14 @@ Editor::time_fx (RegionList& regions, float val, bool pitching)
} }
current_timefx->status = 0; current_timefx->status = 0;
current_timefx->request.time_fraction = current_timefx->get_time_fraction ();
current_timefx->request.pitch_fraction = current_timefx->get_pitch_fraction ();
if (pitching) { if (current_timefx->request.time_fraction == 1.0 &&
current_timefx->request.pitch_fraction == 1.0) {
float cents = current_timefx->pitch_octave_adjustment.get_value() * 1200.0; /* nothing to do */
float pitch_fraction; current_timefx->hide ();
cents += current_timefx->pitch_semitone_adjustment.get_value() * 100.0; return 0;
cents += current_timefx->pitch_cent_adjustment.get_value();
if (cents == 0.0) {
// user didn't change anything
current_timefx->hide ();
return 0;
}
// one octave == 1200 cents
// adding one octave doubles the frequency
// ratio is 2^^octaves
pitch_fraction = pow(2, cents/1200);
current_timefx->request.time_fraction = 1.0;
current_timefx->request.pitch_fraction = pitch_fraction;
} else {
current_timefx->request.time_fraction = val;
current_timefx->request.pitch_fraction = 1.0;
} }
#ifdef USE_RUBBERBAND #ifdef USE_RUBBERBAND
@ -413,4 +389,3 @@ Editor::timefx_thread (void *arg)
#endif #endif
return 0; return 0;
} }

View file

@ -17,7 +17,6 @@
*/ */
#include "time_fx_dialog.h"
#include <iostream> #include <iostream>
#include <cstdlib> #include <cstdlib>
@ -31,10 +30,12 @@
#include <gtkmm2ext/utils.h> #include <gtkmm2ext/utils.h>
#include "audio_clock.h"
#include "editor.h" #include "editor.h"
#include "audio_time_axis.h" #include "audio_time_axis.h"
#include "audio_region_view.h" #include "audio_region_view.h"
#include "region_selection.h" #include "region_selection.h"
#include "time_fx_dialog.h"
#ifdef USE_RUBBERBAND #ifdef USE_RUBBERBAND
#include <rubberband/RubberBandStretcher.h> #include <rubberband/RubberBandStretcher.h>
@ -49,21 +50,27 @@ using namespace PBD;
using namespace Gtk; using namespace Gtk;
using namespace Gtkmm2ext; using namespace Gtkmm2ext;
TimeFXDialog::TimeFXDialog (Editor& e, bool pitch) TimeFXDialog::TimeFXDialog (Editor& e, bool pitch, framecnt_t oldlen, framecnt_t new_length, framepos_t position)
: ArdourDialog (X_("time fx dialog")) : ArdourDialog (X_("time fx dialog"))
, editor (e) , editor (e)
, pitching (pitch) , pitching (pitch)
, quick_button (_("Quick but Ugly"))
, antialias_button (_("Skip Anti-aliasing"))
, stretch_opts_label (_("Contents:"))
, precise_button (_("Minimize time distortion"))
, preserve_formants_button(_("Preserve Formants"))
, 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)
, pitch_cent_adjustment (0.0, -499.0, 500.0, 5.0, 15.0) , pitch_cent_adjustment (0.0, -499.0, 500.0, 5.0, 15.0)
, pitch_octave_spinner (pitch_octave_adjustment) , pitch_octave_spinner (pitch_octave_adjustment)
, pitch_semitone_spinner (pitch_semitone_adjustment) , pitch_semitone_spinner (pitch_semitone_adjustment)
, pitch_cent_spinner (pitch_cent_adjustment) , pitch_cent_spinner (pitch_cent_adjustment)
, quick_button (_("Quick but Ugly")) , percent_adjustment (100.0, -1000.0, 1000.0, 1.0, 10.0)
, antialias_button (_("Skip Anti-aliasing")) , duration_clock (0)
, stretch_opts_label (_("Contents:")) , duration_chosen (_("Duration"))
, precise_button (_("Minimize time distortion")) , choice_group (duration_chosen.get_group())
, preserve_formants_button(_("Preserve Formants")) , percent_chosen (choice_group, _("Percent"))
{ {
set_modal (true); set_modal (true);
set_skip_taskbar_hint (true); set_skip_taskbar_hint (true);
@ -123,31 +130,55 @@ TimeFXDialog::TimeFXDialog (Editor& e, bool pitch)
upper_button_box.pack_start (*table, false, true); upper_button_box.pack_start (*table, false, true);
} else { } else {
Table* table = manage (new Table (2, 3, false)); Table* table = manage (new Table (4, 2, false));
int row = 0;
table->set_row_spacings (6); table->set_row_spacings (6);
table->set_col_spacing (1, 6); table->set_col_spacings (6);
l = manage (new Label ("", Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false ));
l->set_padding (8, 0);
table->attach (*l, 0, 1, 0, 2, Gtk::FILL, Gtk::FILL, 0, 0);
#ifdef USE_RUBBERBAND #ifdef USE_RUBBERBAND
vector<string> strings; vector<string> strings;
duration_clock = manage (new AudioClock (X_("stretch"), true, "stretch", true, false, true, false, true));
duration_clock->set_session (e.session());
duration_clock->set (new_length, true);
duration_clock->set_mode (AudioClock::BBT);
duration_clock->set_bbt_reference (position);
table->attach (stretch_opts_label, 1, 2, 0, 1, Gtk::FILL, Gtk::EXPAND, 0, 0); Gtk::Alignment* clock_align = manage (new Gtk::Alignment);
clock_align->add (*duration_clock);
clock_align->set (0.0, 0.5, 0.0, 1.0);
Gtk::RadioButtonGroup group;
table->attach (duration_chosen, 0, 1, row, row+1, Gtk::FILL, Gtk::FILL, 0, 0);
table->attach (*clock_align, 1, 2, row, row+1, Gtk::AttachOptions (Gtk::EXPAND|Gtk::FILL), Gtk::FILL, 0, 0);
row++;
const double fract = ((double) new_length) / original_length;
/* note the *100.0 to convert fract into a percentage */
percent_adjustment.set_value (fract*100.0);
Gtk::SpinButton* spinner = manage (new Gtk::SpinButton (percent_adjustment, 1.0, 3));
table->attach (percent_chosen, 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);
row++;
table->attach (stretch_opts_label, 0, 1, row, row+1, Gtk::FILL, Gtk::EXPAND, 0, 0);
set_popdown_strings (stretch_opts_selector, editor.rb_opt_strings); set_popdown_strings (stretch_opts_selector, editor.rb_opt_strings);
/* set default */ /* set default */
stretch_opts_selector.set_active_text (editor.rb_opt_strings[editor.rb_current_opt]); stretch_opts_selector.set_active_text (editor.rb_opt_strings[editor.rb_current_opt]);
table->attach (stretch_opts_selector, 2, 3, 0, 1, Gtk::FILL, Gtk::EXPAND & Gtk::FILL, 0, 0); table->attach (stretch_opts_selector, 1, 2, row, row+1, Gtk::FILL, Gtk::EXPAND & Gtk::FILL, 0, 0);
row++;
table->attach (precise_button, 1, 3, 1, 2, Gtk::FILL, Gtk::EXPAND, 0, 0);
table->attach (precise_button, 0, 2, row, row+1, Gtk::FILL, Gtk::EXPAND, 0, 0);
row++;
#else #else
quick_button.set_name (N_("TimeFXButton")); quick_button.set_name (N_("TimeFXButton"));
table->attach (quick_button, 1, 3, 0, 1, Gtk::FILL, Gtk::EXPAND, 0, 0); table->attach (quick_button, 1, 3, row, row+1, Gtk::FILL, Gtk::EXPAND, 0, 0);
row++;
antialias_button.set_name (N_("TimeFXButton")); antialias_button.set_name (N_("TimeFXButton"));
table->attach (antialias_button, 1, 3, 1, 2, Gtk::FILL, Gtk::EXPAND, 0, 0); table->attach (antialias_button, 1, 3, row, row+1, Gtk::FILL, Gtk::EXPAND, 0, 0);
#endif #endif
@ -199,3 +230,39 @@ TimeFXDialog::delete_in_progress (GdkEventAny*)
return TRUE; return TRUE;
} }
float
TimeFXDialog::get_time_fraction () const
{
if (pitching) {
return 1.0;
}
if (duration_chosen.get_active()) {
return duration_clock->current_duration () / original_length;
}
return percent_adjustment.get_value() / 100.0;
}
float
TimeFXDialog::get_pitch_fraction () const
{
if (!pitching) {
return 1.0;
}
float cents = pitch_octave_adjustment.get_value() * 1200.0;
cents += pitch_semitone_adjustment.get_value() * 100.0;
cents += pitch_cent_adjustment.get_value();
if (cents == 0.0) {
return 1.0;
}
// one octave == 1200 cents
// adding one octave doubles the frequency
// ratio is 2^^octaves
return pow(2, cents/1200);
}

View file

@ -1,26 +1,32 @@
/* /*
Copyright (C) 2000-2009 Paul Davis Copyright (C) 2000-2009 Paul Davis
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#ifndef __ardour_time_fx_dialog_h__ #ifndef __ardour_time_fx_dialog_h__
#define __ardour_time_fx_dialog_h__ #define __ardour_time_fx_dialog_h__
#include <gtkmm.h> #include <gtkmm/adjustment.h>
#include <gtkmm/spinbutton.h>
#include <gtkmm/progressbar.h>
#include <gtkmm/box.h>
#include <gtkmm/comboboxtext.h>
#include <gtkmm/label.h>
#include <gtkmm/button.h>
#include "ardour/timefx_request.h" #include "ardour/timefx_request.h"
@ -28,48 +34,58 @@
#include "progress_reporter.h" #include "progress_reporter.h"
class Editor; class Editor;
class AudioClock;
class TimeFXDialog : public ArdourDialog, public ProgressReporter class TimeFXDialog : public ArdourDialog, public ProgressReporter
{ {
public: public:
ARDOUR::TimeFXRequest request; /* We need a position so that BBT mode in the clock can function */
Editor& editor; TimeFXDialog (Editor& e, bool for_pitch, ARDOUR::framecnt_t old_length, ARDOUR::framecnt_t new_length, ARDOUR::framepos_t position);
bool pitching;
Gtk::Adjustment pitch_octave_adjustment;
Gtk::Adjustment pitch_semitone_adjustment;
Gtk::Adjustment pitch_cent_adjustment;
Gtk::SpinButton pitch_octave_spinner;
Gtk::SpinButton pitch_semitone_spinner;
Gtk::SpinButton pitch_cent_spinner;
Gtk::ProgressBar progress_bar;
ARDOUR::RegionList regions;
/* SoundTouch */ ARDOUR::TimeFXRequest request;
Gtk::CheckButton quick_button; Editor& editor;
Gtk::CheckButton antialias_button; bool pitching;
Gtk::VBox upper_button_box; Gtk::ProgressBar progress_bar;
ARDOUR::RegionList regions;
/* RubberBand */ /* SoundTouch */
Gtk::ComboBoxText stretch_opts_selector; Gtk::CheckButton quick_button;
Gtk::Label stretch_opts_label; Gtk::CheckButton antialias_button;
Gtk::CheckButton precise_button; Gtk::VBox upper_button_box;
Gtk::CheckButton preserve_formants_button;
Gtk::Button* cancel_button; /* RubberBand */
Gtk::Button* action_button; Gtk::ComboBoxText stretch_opts_selector;
Gtk::VBox packer; Gtk::Label stretch_opts_label;
int status; Gtk::CheckButton precise_button;
Gtk::CheckButton preserve_formants_button;
TimeFXDialog (Editor& e, bool for_pitch); Gtk::Button* cancel_button;
Gtk::Button* action_button;
Gtk::VBox packer;
int status;
sigc::connection first_cancel; sigc::connection first_cancel;
sigc::connection first_delete; sigc::connection first_delete;
void cancel_in_progress (); void cancel_in_progress ();
gint delete_in_progress (GdkEventAny*); gint delete_in_progress (GdkEventAny*);
private: float get_time_fraction () const;
float get_pitch_fraction () const;
void update_progress_gui (float); private:
ARDOUR::framecnt_t original_length;
Gtk::Adjustment pitch_octave_adjustment;
Gtk::Adjustment pitch_semitone_adjustment;
Gtk::Adjustment pitch_cent_adjustment;
Gtk::SpinButton pitch_octave_spinner;
Gtk::SpinButton pitch_semitone_spinner;
Gtk::SpinButton pitch_cent_spinner;
Gtk::Adjustment percent_adjustment;
AudioClock* duration_clock;
Gtk::RadioButton duration_chosen;
Gtk::RadioButtonGroup choice_group;
Gtk::RadioButton percent_chosen;
void update_progress_gui (float);
}; };
#endif /* __ardour_time_fx_dialog_h__ */ #endif /* __ardour_time_fx_dialog_h__ */