X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Ftime_fx_dialog.cc;h=c51bdcd5e2b22d7ef04974dd250e4abd303f0a42;hb=d53af10c9261172c57dbc66b6b4d1143b37bbcae;hp=b20c01c4e1c517067f25785471e38b69deda69db;hpb=eeae6aec340d74eba7f5d00e450cbe47afcf0ec0;p=ardour.git diff --git a/gtk2_ardour/time_fx_dialog.cc b/gtk2_ardour/time_fx_dialog.cc index b20c01c4e1..c51bdcd5e2 100644 --- a/gtk2_ardour/time_fx_dialog.cc +++ b/gtk2_ardour/time_fx_dialog.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2000-2009 Paul Davis + Copyright (C) 2000-2009 Paul Davis 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 @@ -17,157 +17,230 @@ */ -#include "time_fx_dialog.h" #include #include #include - #include +#include + #include "pbd/error.h" #include "pbd/pthread_utils.h" #include "pbd/memento_command.h" +#include "pbd/unwind.h" +#include "pbd/stacktrace.h" -#include -#include +#include "gtkmm2ext/utils.h" +#include "audio_clock.h" #include "editor.h" #include "audio_time_axis.h" #include "audio_region_view.h" #include "region_selection.h" - -#include "ardour/session.h" -#include "ardour/region.h" -#include "ardour/audioplaylist.h" -#include "ardour/audio_track.h" -#include "ardour/audioregion.h" -#include "ardour/audio_diskstream.h" -#include "ardour/stretch.h" -#include "ardour/midi_stretch.h" -#include "ardour/pitch.h" +#include "time_fx_dialog.h" +#include "timers.h" #ifdef USE_RUBBERBAND -#include "rubberband/RubberBandStretcher.h" +#include using namespace RubberBand; #endif -#include "i18n.h" +#include "pbd/i18n.h" using namespace std; using namespace ARDOUR; using namespace PBD; -using namespace sigc; using namespace Gtk; using namespace Gtkmm2ext; -TimeFXDialog::TimeFXDialog (Editor& e, bool pitch) +TimeFXDialog::TimeFXDialog (Editor& e, bool pitch, samplecnt_t oldlen, samplecnt_t new_length, samplepos_t position) : ArdourDialog (X_("time fx dialog")) , editor (e) , 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_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_octave_spinner (pitch_octave_adjustment) , pitch_semitone_spinner (pitch_semitone_adjustment) , pitch_cent_spinner (pitch_cent_adjustment) - , quick_button (_("Quick but Ugly")) - , antialias_button (_("Skip Anti-aliasing")) - , stretch_opts_label (_("Contents:")) - , precise_button (_("Strict Linear")) - , preserve_formants_button(_("Preserve Formants")) + , duration_adjustment (100.0, -1000.0, 1000.0, 1.0, 10.0) + , duration_clock (0) + , ignore_adjustment_change (false) + , ignore_clock_change (false) + , progress (0.0f) { set_modal (true); - set_position (Gtk::WIN_POS_MOUSE); + set_skip_taskbar_hint (true); + set_resizable (false); set_name (N_("TimeFXDialog")); - WindowTitle title(Glib::get_application_name()); if (pitching) { - title += _("Pitch Shift"); + set_title (_("Pitch Shift Audio")); } else { - title += _("Time Stretch"); + set_title (_("Time Stretch Audio")); } - set_title(title.get_string()); - cancel_button = add_button (_("Cancel"), Gtk::RESPONSE_CANCEL); + cancel_button = add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); - get_vbox()->set_spacing (5); - get_vbox()->set_border_width (12); + VBox* vbox = manage (new VBox); + Gtk::Label* l; - if (pitching) { + get_vbox()->set_spacing (4); - upper_button_box.set_spacing (5); - upper_button_box.set_border_width (5); - - Gtk::Label* l; + vbox->set_spacing (18); + vbox->set_border_width (5); - l = manage (new Label (_("Octaves"))); - upper_button_box.pack_start (*l, false, false); - upper_button_box.pack_start (pitch_octave_spinner, false, false); + upper_button_box.set_spacing (6); - l = manage (new Label (_("Semitones (12TET)"))); - upper_button_box.pack_start (*l, false, false); - upper_button_box.pack_start (pitch_semitone_spinner, false, false); + l = manage (new Label (_("Options"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false )); + l->set_use_markup (); - l = manage (new Label (_("Cents"))); - upper_button_box.pack_start (*l, false, false); - upper_button_box.pack_start (pitch_cent_spinner, false, false); + upper_button_box.pack_start (*l, false, false); + if (pitching) { + Table* table = manage (new Table (4, 3, false)); + table->set_row_spacings (6); + table->set_col_spacing (1, 6); + l = manage (new Label ("", Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false )); //Common gnome way for padding + l->set_padding (8, 0); + table->attach (*l, 0, 1, 0, 4, Gtk::FILL, Gtk::FILL, 0, 0); + + l = manage (new Label (_("Octaves:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false)); + table->attach (*l, 1, 2, 0, 1, Gtk::FILL, Gtk::EXPAND, 0, 0); + table->attach (pitch_octave_spinner, 2, 3, 0, 1, Gtk::FILL, Gtk::EXPAND & Gtk::FILL, 0, 0); + pitch_octave_spinner.set_activates_default (); + + l = manage (new Label (_("Semitones:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false)); + table->attach (*l, 1, 2, 1, 2, Gtk::FILL, Gtk::EXPAND, 0, 0); + table->attach (pitch_semitone_spinner, 2, 3, 1, 2, Gtk::FILL, Gtk::EXPAND & Gtk::FILL, 0, 0); + pitch_semitone_spinner.set_activates_default (); + + l = manage (new Label (_("Cents:"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false)); pitch_cent_spinner.set_digits (1); + table->attach (*l, 1, 2, 2, 3, Gtk::FILL, Gtk::EXPAND, 0, 0); + table->attach (pitch_cent_spinner, 2, 3, 2, 3, Gtk::FILL, Gtk::EXPAND & Gtk::FILL, 0, 0); + pitch_cent_spinner.set_activates_default (); - upper_button_box.pack_start (preserve_formants_button, false, false); - + table->attach (preserve_formants_button, 1, 3, 3, 4, Gtk::FILL, Gtk::EXPAND, 0, 0); - add_button (_("Shift"), Gtk::RESPONSE_ACCEPT); - - get_vbox()->pack_start (upper_button_box, false, false); + add_button (S_("Time|Shift"), Gtk::RESPONSE_ACCEPT); + upper_button_box.pack_start (*table, false, true); } else { + Table* table = manage (new Table (4, 2, false)); + int row = 0; + + table->set_row_spacings (6); + table->set_col_spacings (12); #ifdef USE_RUBBERBAND - opts_box.set_spacing (5); - opts_box.set_border_width (5); vector strings; + duration_clock = manage (new AudioClock (X_("stretch"), true, X_("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); + + Gtk::Alignment* clock_align = manage (new Gtk::Alignment); + clock_align->add (*duration_clock); + clock_align->set (0.0, 0.5, 0.0, 1.0); + + l = manage (new Gtk::Label (_("Duration"))); + table->attach (*l, 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 */ + duration_adjustment.set_value (fract*100.0); + Gtk::SpinButton* spinner = manage (new Gtk::SpinButton (duration_adjustment, 1.0, 3)); + + 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); + 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 default */ - stretch_opts_selector.set_active_text (editor.rb_opt_strings[4]); + stretch_opts_selector.set_active_text (editor.rb_opt_strings[editor.rb_current_opt]); + table->attach (stretch_opts_selector, 1, 2, row, row+1, Gtk::FILL, Gtk::EXPAND & Gtk::FILL, 0, 0); + row++; - opts_box.pack_start (precise_button, false, false); - opts_box.pack_start (stretch_opts_label, false, false); - opts_box.pack_start (stretch_opts_selector, false, false); + table->attach (precise_button, 0, 2, row, row+1, Gtk::FILL, Gtk::EXPAND, 0, 0); + row++; - get_vbox()->pack_start (opts_box, false, false); + duration_clock->ValueChanged.connect (sigc::mem_fun (*this, &TimeFXDialog::duration_clock_changed)); + duration_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &TimeFXDialog::duration_adjustment_changed)); #else - upper_button_box.set_homogeneous (true); - upper_button_box.set_spacing (5); - upper_button_box.set_border_width (5); - - upper_button_box.pack_start (quick_button, true, true); - upper_button_box.pack_start (antialias_button, true, true); - quick_button.set_name (N_("TimeFXButton")); + table->attach (quick_button, 1, 3, row, row+1, Gtk::FILL, Gtk::EXPAND, 0, 0); + row++; + antialias_button.set_name (N_("TimeFXButton")); + table->attach (antialias_button, 1, 3, row, row+1, Gtk::FILL, Gtk::EXPAND, 0, 0); - get_vbox()->pack_start (upper_button_box, false, false); +#endif -#endif add_button (_("Stretch/Shrink"), Gtk::RESPONSE_ACCEPT); + + upper_button_box.pack_start (*table, false, true); } - get_vbox()->pack_start (progress_bar); + set_default_response (Gtk::RESPONSE_ACCEPT); + + VBox* progress_box = manage (new VBox); + progress_box->set_spacing (6); - progress_bar.set_name (N_("TimeFXProgress")); + l = manage (new Label (_("Progress"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false)); + l->set_use_markup (); + + progress_box->pack_start (*l, false, false); + progress_box->pack_start (progress_bar, false, true); + + + vbox->pack_start (upper_button_box, false, true); + vbox->pack_start (*progress_box, false, true); + + get_vbox()->pack_start (*vbox, false, false); show_all_children (); } -gint -TimeFXDialog::update_progress () +void +TimeFXDialog::start_updates () +{ + update_connection = Timers::rapid_connect (sigc::mem_fun (*this, &TimeFXDialog::timer_update)); +} + +void +TimeFXDialog::update_progress_gui (float p) { - progress_bar.set_fraction (request.progress); - return !request.done; + /* time/pitch FX are applied in a dedicated thread, so we cannot just + update the GUI when notified about progress. That is deferred to a + timer-driven callback which will ensure that the visual progress + indicator is updated. + */ + progress = p; +} + +void +TimeFXDialog::timer_update () +{ + progress_bar.set_fraction (progress); + + if (request.done || request.cancel) { + update_connection.disconnect (); + } } void @@ -179,7 +252,7 @@ TimeFXDialog::cancel_in_progress () } gint -TimeFXDialog::delete_in_progress (GdkEventAny* ev) +TimeFXDialog::delete_in_progress (GdkEventAny*) { status = -2; request.cancel = true; @@ -187,3 +260,59 @@ TimeFXDialog::delete_in_progress (GdkEventAny* ev) return TRUE; } +float +TimeFXDialog::get_time_fraction () const +{ + if (pitching) { + return 1.0; + } + + return duration_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); +} + +void +TimeFXDialog::duration_adjustment_changed () +{ + if (ignore_adjustment_change) { + return; + } + + PBD::Unwinder uw (ignore_clock_change, true); + + duration_clock->set ((samplecnt_t) (original_length * (duration_adjustment.get_value()/ 100.0))); +} + +void +TimeFXDialog::duration_clock_changed () +{ + if (ignore_clock_change) { + return; + } + + PBD::Unwinder uw (ignore_adjustment_change, true); + + duration_adjustment.set_value (100.0 * (duration_clock->current_duration() / (double) original_length)); +}