diff options
-rw-r--r-- | gtk2_ardour/ardour-sae.bindings.in | 4 | ||||
-rw-r--r-- | gtk2_ardour/ardour-sae.menus | 4 | ||||
-rw-r--r-- | gtk2_ardour/ardour_ui.cc | 2 | ||||
-rw-r--r-- | gtk2_ardour/editor.cc | 2 | ||||
-rw-r--r-- | gtk2_ardour/editor.h | 32 | ||||
-rw-r--r-- | gtk2_ardour/editor_actions.cc | 5 | ||||
-rw-r--r-- | gtk2_ardour/editor_keyboard.cc | 11 | ||||
-rw-r--r-- | gtk2_ardour/editor_mouse.cc | 2 | ||||
-rw-r--r-- | gtk2_ardour/editor_ops.cc | 24 | ||||
-rw-r--r-- | gtk2_ardour/editor_timefx.cc | 205 | ||||
-rw-r--r-- | libs/ardour/SConscript | 6 | ||||
-rw-r--r-- | libs/ardour/ardour/pitch.h | 62 | ||||
-rw-r--r-- | libs/ardour/ardour/region.h | 4 | ||||
-rw-r--r-- | libs/ardour/ardour/stretch.h | 37 | ||||
-rw-r--r-- | libs/ardour/ardour/types.h | 7 | ||||
-rw-r--r-- | libs/ardour/rb_effect.cc (renamed from libs/ardour/rb_pitch.cc) | 68 | ||||
-rw-r--r-- | libs/ardour/region.cc | 14 | ||||
-rw-r--r-- | libs/ardour/st_pitch.cc | 56 | ||||
-rw-r--r-- | libs/ardour/st_stretch.cc | 6 |
19 files changed, 424 insertions, 127 deletions
diff --git a/gtk2_ardour/ardour-sae.bindings.in b/gtk2_ardour/ardour-sae.bindings.in index 2449abd00e..78af95aa15 100644 --- a/gtk2_ardour/ardour-sae.bindings.in +++ b/gtk2_ardour/ardour-sae.bindings.in @@ -66,7 +66,7 @@ (gtk_accel_path "<Actions>/Editor/jump-backward-to-mark" "<%PRIMARY%>KP_Left") ; (gtk_accel_path "<Actions>/Main/AudioFileFormatData" "") ; (gtk_accel_path "<Actions>/options/MeterFalloffFastest" "") -; (gtk_accel_path "<Actions>/Editor/audition-at-mouse" "w") +(gtk_accel_path "<Actions>/Editor/audition-region" "w") (gtk_accel_path "<Actions>/Transport/Forward" "<%PRIMARY%>rightarrow") ; (gtk_accel_path "<Actions>/Snap/snap-to-smpte-seconds" "") ; (gtk_accel_path "<Actions>/Snap/snap-to-smpte-frame" "") @@ -340,5 +340,5 @@ (gtk_accel_path "<Actions>/Editor/loop-region" "<%PRIMARY%>bracketright") (gtk_accel_path "<Actions>/Editor/toggle-zoom" "o") (gtk_accel_path "<Actions>/Editor/zoom-to-region" "y") - +(gtk_accel_path "<Actions>/Editor/pitch-shift-region" "F5") diff --git a/gtk2_ardour/ardour-sae.menus b/gtk2_ardour/ardour-sae.menus index 62d3447920..fa5c45914f 100644 --- a/gtk2_ardour/ardour-sae.menus +++ b/gtk2_ardour/ardour-sae.menus @@ -106,7 +106,7 @@ <menuitem action='edit-to-playhead'/> </menu> <menu name='KeyMouse Actions' action='KeyMouse Actions'> - <menuitem action='audition-at-mouse'/> + <menuitem action='audition-region'/> <menuitem action='brush-at-mouse'/> <menuitem action='mute-unmute-region'/> <separator/> @@ -148,6 +148,8 @@ <menuitem action='trim-back'/> <menuitem action='trim-region-to-loop'/> <menuitem action='trim-region-to-punch'/> + <separator/> + <menuitem action='pitch-shift-region'/> </menu> <menu name='View' action = 'View'> <menuitem action='ToggleMaximalEditor'/> diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 44562f3ec0..033cc5ed3c 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -2028,6 +2028,8 @@ ARDOUR_UI::get_session_parameters (Glib::ustring predetermined_path, bool have_e new_session_dialog->show(); new_session_dialog->present (); response = new_session_dialog->run (); + + loading_dialog->hide (); _session_is_new = false; diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index e3b5796045..ba824512df 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -302,7 +302,7 @@ Editor::Editor () entered_marker = 0; clear_entered_track = false; _new_regionviews_show_envelope = false; - current_timestretch = 0; + current_timefx = 0; in_edit_group_row_change = false; last_canvas_frame = 0; playhead_cursor = 0; diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 63a19e0d3e..30cfed686d 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -1290,7 +1290,6 @@ class Editor : public PublicEditor void kbd_driver (sigc::slot<void,GdkEvent*>, bool use_track_canvas = true, bool use_time_canvas = true, bool can_select = true); void kbd_mute_unmute_region (); void kbd_brush (); - void kbd_audition (); void kbd_do_brush (GdkEvent*); void kbd_do_audition (GdkEvent*); @@ -1806,9 +1805,16 @@ class Editor : public PublicEditor void start_time_fx (ArdourCanvas::Item*, GdkEvent*); void end_time_fx (ArdourCanvas::Item*, GdkEvent*); - struct TimeStretchDialog : public ArdourDialog { - ARDOUR::TimeStretchRequest request; + struct TimeFXDialog : public ArdourDialog { + ARDOUR::TimeFXRequest request; Editor& editor; + 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; RegionSelection regions; Gtk::ProgressBar progress_bar; Gtk::ToggleButton quick_button; @@ -1819,24 +1825,28 @@ class Editor : public PublicEditor Gtk::VBox packer; int status; - TimeStretchDialog (Editor& e); + TimeFXDialog (Editor& e, bool for_pitch); gint update_progress (); sigc::connection first_cancel; sigc::connection first_delete; - void cancel_timestretch_in_progress (); - gint delete_timestretch_in_progress (GdkEventAny*); + void cancel_in_progress (); + gint delete_in_progress (GdkEventAny*); }; /* "whats mine is yours" */ - friend class TimeStretchDialog; + friend class TimeFXDialog; - TimeStretchDialog* current_timestretch; + TimeFXDialog* current_timefx; - static void* timestretch_thread (void *arg); - int run_timestretch (RegionSelection&, float fraction); - void do_timestretch (TimeStretchDialog&); + static void* timefx_thread (void *arg); + void do_timefx (TimeFXDialog&); + + int time_stretch (RegionSelection&, float fraction); + int pitch_shift (RegionSelection&, float cents); + void pitch_shift_regions (); + int time_fx (RegionSelection&, float val, bool pitching); /* editor-mixer strip */ diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 487d354a0a..2d2e91600c 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -267,6 +267,9 @@ Editor::register_actions () ActionManager::session_sensitive_actions.push_back (act); + act = ActionManager::register_action (editor_actions, "pitch-shift-region", _("Transpose"), mem_fun(*this, &Editor::pitch_shift_regions)); + ActionManager::session_sensitive_actions.push_back (act); + act = ActionManager::register_action (editor_actions, "set-fade-in-length", _("Set Fade In Length"), bind (mem_fun(*this, &Editor::set_fade_length), true)); ActionManager::session_sensitive_actions.push_back (act); act = ActionManager::register_action (editor_actions, "toggle-fade-in-active", _("Toggle Fade In Active"), bind (mem_fun(*this, &Editor::toggle_fade_active), true)); @@ -290,7 +293,7 @@ Editor::register_actions () act = ActionManager::register_action (editor_actions, "align-regions-sync-relative", _("Align Regions Sync Relative"), bind (mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint)); ActionManager::session_sensitive_actions.push_back (act); - act = ActionManager::register_action (editor_actions, "audition-at-mouse", _("Audition at Mouse"), mem_fun(*this, &Editor::kbd_audition)); + act = ActionManager::register_action (editor_actions, "audition-region", _("Audition Region"), mem_fun(*this, &Editor::audition_selected_region)); ActionManager::session_sensitive_actions.push_back (act); act = ActionManager::register_action (editor_actions, "brush-at-mouse", _("Brush at Mouse"), mem_fun(*this, &Editor::kbd_brush)); ActionManager::session_sensitive_actions.push_back (act); diff --git a/gtk2_ardour/editor_keyboard.cc b/gtk2_ardour/editor_keyboard.cc index 7f2b892fc6..4a5d55d879 100644 --- a/gtk2_ardour/editor_keyboard.cc +++ b/gtk2_ardour/editor_keyboard.cc @@ -102,14 +102,3 @@ Editor::kbd_brush () kbd_driver (mem_fun(*this, &Editor::kbd_do_brush), true, true, false); } -void -Editor::kbd_do_audition (GdkEvent *ignored) -{ - audition_selected_region (); -} - -void -Editor::kbd_audition () -{ - kbd_driver (mem_fun(*this, &Editor::kbd_do_audition), true, false, true); -} diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index f6e3301164..f65325c2de 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -5166,7 +5166,7 @@ Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event) begin_reversible_command (_("timestretch")); - if (run_timestretch (selection->regions, percentage) == 0) { + if (time_stretch (selection->regions, percentage) == 0) { session->commit_reversible_command (); } } diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 5edd0e15c4..6814dda081 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -2258,9 +2258,15 @@ Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Ro void Editor::audition_selected_region () { - if (!selection->regions.empty()) { - RegionView* rv = *(selection->regions.begin()); - session->audition_region (rv->region()); + ensure_entered_region_selected (true); + + if (selection->regions.empty()) { + return; + } + + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { + session->audition_region ((*i)->region()); + /* XXX need to check if user requested stop between each one, but how? */ } } @@ -4600,3 +4606,15 @@ Editor::set_punch_from_edit_range () set_punch_range (start, end, _("set punch range from edit range")); } +void +Editor::pitch_shift_regions () +{ + ensure_entered_region_selected (true); + + if (selection->regions.empty()) { + return; + } + + pitch_shift (selection->regions, 1.2); +} + diff --git a/gtk2_ardour/editor_timefx.cc b/gtk2_ardour/editor_timefx.cc index df0a73c965..b87c80b38e 100644 --- a/gtk2_ardour/editor_timefx.cc +++ b/gtk2_ardour/editor_timefx.cc @@ -40,6 +40,7 @@ #include <ardour/audioregion.h> #include <ardour/audio_diskstream.h> #include <ardour/stretch.h> +#include <ardour/pitch.h> #include "i18n.h" @@ -49,50 +50,88 @@ using namespace sigc; using namespace Gtk; using namespace Gtkmm2ext; -Editor::TimeStretchDialog::TimeStretchDialog (Editor& e) - : ArdourDialog ("time stretch dialog"), +Editor::TimeFXDialog::TimeFXDialog (Editor& e, bool pitch) + : ArdourDialog (X_("time fx dialog")), editor (e), + pitching (pitch), + pitch_octave_adjustment (0.0, 0.0, 4.0, 1, 2.0), + pitch_semitone_adjustment (0.0, 0.0, 12.0, 1.0, 4.0), + pitch_cent_adjustment (0.0, 0.0, 150.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")) { set_modal (true); set_position (Gtk::WIN_POS_MOUSE); - set_name (N_("TimeStretchDialog")); + set_name (N_("TimeFXDialog")); WindowTitle title(Glib::get_application_name()); - title += _("Timestretch"); + if (pitching) { + title += _("Pitch Shift"); + } else { + title += _("Time Stretch"); + } set_title(title.get_string()); + cancel_button = add_button (_("Cancel"), Gtk::RESPONSE_CANCEL); + get_vbox()->set_spacing (5); - get_vbox()->set_border_width (5); - get_vbox()->pack_start (upper_button_box); + get_vbox()->set_border_width (12); + get_vbox()->pack_start (upper_button_box, false, false); get_vbox()->pack_start (progress_bar); - 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); + if (pitching) { - action_button = add_button (_("Stretch/Shrink it"), Gtk::RESPONSE_ACCEPT); - cancel_button = add_button (_("Cancel"), Gtk::RESPONSE_CANCEL); + upper_button_box.set_spacing (5); + upper_button_box.set_border_width (5); + + Gtk::Label* l; + + l = manage (new Label (_("Octaves"))); + upper_button_box.pack_start (*l, false, false); + upper_button_box.pack_start (pitch_octave_spinner, false, false); + + 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 (_("Cents"))); + upper_button_box.pack_start (*l, false, false); + upper_button_box.pack_start (pitch_cent_spinner, false, false); + + pitch_cent_spinner.set_digits (1); + + add_button (_("Shift"), Gtk::RESPONSE_ACCEPT); - quick_button.set_name (N_("TimeStretchButton")); - antialias_button.set_name (N_("TimeStretchButton")); - progress_bar.set_name (N_("TimeStretchProgress")); + } 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); + + add_button (_("Stretch/Shrink"), Gtk::RESPONSE_ACCEPT); + } + + quick_button.set_name (N_("TimeFXButton")); + antialias_button.set_name (N_("TimeFXButton")); + progress_bar.set_name (N_("TimeFXProgress")); show_all_children (); } gint -Editor::TimeStretchDialog::update_progress () +Editor::TimeFXDialog::update_progress () { progress_bar.set_fraction (request.progress); return !request.done; } void -Editor::TimeStretchDialog::cancel_timestretch_in_progress () +Editor::TimeFXDialog::cancel_in_progress () { status = -2; request.cancel = true; @@ -100,7 +139,7 @@ Editor::TimeStretchDialog::cancel_timestretch_in_progress () } gint -Editor::TimeStretchDialog::delete_timestretch_in_progress (GdkEventAny* ev) +Editor::TimeFXDialog::delete_in_progress (GdkEventAny* ev) { status = -2; request.cancel = true; @@ -109,63 +148,109 @@ Editor::TimeStretchDialog::delete_timestretch_in_progress (GdkEventAny* ev) } int -Editor::run_timestretch (RegionSelection& regions, float fraction) +Editor::time_stretch (RegionSelection& regions, float fraction) +{ + return time_fx (regions, fraction, false); +} + +int +Editor::pitch_shift (RegionSelection& regions, float fraction) { - if (current_timestretch == 0) { - current_timestretch = new TimeStretchDialog (*this); + return time_fx (regions, fraction, true); +} + +int +Editor::time_fx (RegionSelection& regions, float val, bool pitching) +{ + if (current_timefx != 0) { + delete current_timefx; } - current_timestretch->progress_bar.set_fraction (0.0f); + current_timefx = new TimeFXDialog (*this, pitching); + + current_timefx->progress_bar.set_fraction (0.0f); - switch (current_timestretch->run ()) { + switch (current_timefx->run ()) { case RESPONSE_ACCEPT: break; default: - current_timestretch->hide (); + current_timefx->hide (); return 1; } - current_timestretch->status = 0; - current_timestretch->regions = regions; - current_timestretch->request.fraction = fraction; - current_timestretch->request.quick_seek = current_timestretch->quick_button.get_active(); - current_timestretch->request.antialias = !current_timestretch->antialias_button.get_active(); - current_timestretch->request.progress = 0.0f; - current_timestretch->request.done = false; - current_timestretch->request.cancel = false; + current_timefx->status = 0; + current_timefx->regions = regions; + + if (pitching) { + + float cents = current_timefx->pitch_octave_adjustment.get_value() * 1200.0; + cents += current_timefx->pitch_semitone_adjustment.get_value() * 100.0; + cents += current_timefx->pitch_cent_adjustment.get_value(); + + if (cents == 0.0) { + // user didn't change anything + current_timefx->hide (); + return 0; + } + + // we now have the pitch shift in cents. divide by 1200 to get octaves + // then multiply by 2.0 because 1 octave == doubling the frequency + + cents /= 1200.0; + cents /= 2.0; + + // add 1.0 to convert to RB scale + + cents += 1.0; + + current_timefx->request.time_fraction = 1.0; + current_timefx->request.pitch_fraction = cents; + + } else { + + current_timefx->request.time_fraction = val; + current_timefx->request.pitch_fraction = 1.0; + + } + + current_timefx->request.quick_seek = current_timefx->quick_button.get_active(); + current_timefx->request.antialias = !current_timefx->antialias_button.get_active(); + current_timefx->request.progress = 0.0f; + current_timefx->request.done = false; + current_timefx->request.cancel = false; /* re-connect the cancel button and delete events */ - current_timestretch->first_cancel.disconnect(); - current_timestretch->first_delete.disconnect(); + current_timefx->first_cancel.disconnect(); + current_timefx->first_delete.disconnect(); - current_timestretch->first_cancel = current_timestretch->cancel_button->signal_clicked().connect - (mem_fun (current_timestretch, &TimeStretchDialog::cancel_timestretch_in_progress)); - current_timestretch->first_delete = current_timestretch->signal_delete_event().connect - (mem_fun (current_timestretch, &TimeStretchDialog::delete_timestretch_in_progress)); - - if (pthread_create_and_store ("timestretch", ¤t_timestretch->request.thread, 0, timestretch_thread, current_timestretch)) { - current_timestretch->hide (); - error << _("timestretch cannot be started - thread creation error") << endmsg; + current_timefx->first_cancel = current_timefx->cancel_button->signal_clicked().connect + (mem_fun (current_timefx, &TimeFXDialog::cancel_in_progress)); + current_timefx->first_delete = current_timefx->signal_delete_event().connect + (mem_fun (current_timefx, &TimeFXDialog::delete_in_progress)); + + if (pthread_create_and_store ("timefx", ¤t_timefx->request.thread, 0, timefx_thread, current_timefx)) { + current_timefx->hide (); + error << _("timefx cannot be started - thread creation error") << endmsg; return -1; } - pthread_detach (current_timestretch->request.thread); + pthread_detach (current_timefx->request.thread); - sigc::connection c = Glib::signal_timeout().connect (mem_fun (current_timestretch, &TimeStretchDialog::update_progress), 100); + sigc::connection c = Glib::signal_timeout().connect (mem_fun (current_timefx, &TimeFXDialog::update_progress), 100); - while (!current_timestretch->request.done) { + while (!current_timefx->request.done) { gtk_main_iteration (); } c.disconnect (); - current_timestretch->hide (); - return current_timestretch->status; + current_timefx->hide (); + return current_timefx->status; } void -Editor::do_timestretch (TimeStretchDialog& dialog) +Editor::do_timefx (TimeFXDialog& dialog) { Track* t; boost::shared_ptr<Playlist> playlist; @@ -205,16 +290,23 @@ Editor::do_timestretch (TimeStretchDialog& dialog) return; } - Stretch stretch (*session, dialog.request); + AudioFilter* fx; + + if (dialog.pitching) { + fx = new Pitch (*session, dialog.request); + } else { + fx = new Stretch (*session, dialog.request); + } - if (stretch.run (region)) { + if (fx->run (region)) { dialog.status = -1; dialog.request.done = true; + delete fx; return; } - if (!stretch.results.empty()) { - new_region = stretch.results.front(); + if (!fx->results.empty()) { + new_region = fx->results.front(); XMLNode &before = playlist->get_state(); playlist->replace_region (region, new_region, region->position()); @@ -223,6 +315,7 @@ Editor::do_timestretch (TimeStretchDialog& dialog) } i = tmp; + delete fx; } dialog.status = 0; @@ -230,15 +323,15 @@ Editor::do_timestretch (TimeStretchDialog& dialog) } void* -Editor::timestretch_thread (void *arg) +Editor::timefx_thread (void *arg) { PBD::ThreadCreated (pthread_self(), X_("TimeFX")); - TimeStretchDialog* tsd = static_cast<TimeStretchDialog*>(arg); + TimeFXDialog* tsd = static_cast<TimeFXDialog*>(arg); pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0); - tsd->editor.do_timestretch (*tsd); + tsd->editor.do_timefx (*tsd); return 0; } diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index d29f4a0b10..5f33eb01f8 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -267,10 +267,10 @@ ardour.Merge ([ if ardour['RUBBERBAND']: ardour.Merge ([ libraries['rubberband'], libraries['vamp'], libraries['fftw3f'] ]) - timefx_sources += [ 'rb_stretch.cc' ] + timefx_sources += [ 'rb_effect.cc' ] else: ardour.Merge ([ libraries['soundtouch'] ]) - timefx_sources += [ 'st_stretch.cc' ] + timefx_sources += [ 'st_stretch.cc', 'st_pitch.cc' ] if ardour['LIBLO']: ardour.Merge ([ libraries['lo'] ]) @@ -326,6 +326,6 @@ env.Alias('version', ardour.VersionBuild(['version.cc', 'ardour/version.h'], []) env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript', 'i18n.h', 'gettext.h' ] + [ 'sse_functions_xmm.cc', 'sse_functions.s', 'sse_functions_64bit.s' ] + - [ 'rb_stretch.cc', 'st_stretch.cc' ] + + [ 'rb_effect.cc', 'st_stretch.cc', 'st_pitch.cc' ] + ardour_files + osc_files + vst_files + coreaudio_files + audiounit_files + glob.glob('po/*.po') + glob.glob('ardour/*.h'))) diff --git a/libs/ardour/ardour/pitch.h b/libs/ardour/ardour/pitch.h new file mode 100644 index 0000000000..785a429625 --- /dev/null +++ b/libs/ardour/ardour/pitch.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2007 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_pitch_h__ +#define __ardour_pitch_h__ + +#include <ardour/audiofilter.h> + +namespace ARDOUR { + class AudioRegion; +} + +#ifdef USE_RUBBERBAND + +#include <ardour/rb_effect.h> + +namespace ARDOUR { + +class Pitch : public RBEffect { + public: + Pitch (ARDOUR::Session&, TimeFXRequest&); + ~Pitch () {} +}; + +} /* namespace */ + +# else + +namespace ARDOUR { + +class Pitch : public AudioFilter { + public: + Pitch (ARDOUR::Session&, TimeFXRequest&); + ~Pitch () {} + + int run (boost::shared_ptr<ARDOUR::AudioRegion>); + + private: + TimeFXRequest& tsr; +}; + +} /* namespace */ + +#endif + +#endif /* __ardour_pitch_h__ */ diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h index 8acbc8b39b..3aae8c4767 100644 --- a/libs/ardour/ardour/region.h +++ b/libs/ardour/ardour/region.h @@ -98,8 +98,9 @@ class Region : public PBD::StatefulDestructible, public boost::enable_shared_fro nframes64_t ancestral_start () const { return _ancestral_start; } nframes64_t ancestral_length () const { return _ancestral_length; } float stretch() const { return _stretch; } + float shift() const { return _shift; } - void set_ancestral_data (nframes64_t start, nframes64_t length, float stretch); + void set_ancestral_data (nframes64_t start, nframes64_t length, float stretch, float shift); nframes_t sync_offset(int& dir) const; nframes_t sync_position() const; @@ -235,6 +236,7 @@ class Region : public PBD::StatefulDestructible, public boost::enable_shared_fro nframes64_t _ancestral_start; nframes64_t _ancestral_length; float _stretch; + float _shift; }; } /* namespace ARDOUR */ diff --git a/libs/ardour/ardour/stretch.h b/libs/ardour/ardour/stretch.h index 6b5c2957e1..0ec926916c 100644 --- a/libs/ardour/ardour/stretch.h +++ b/libs/ardour/ardour/stretch.h @@ -22,36 +22,45 @@ #include <ardour/audiofilter.h> -#ifndef USE_RUBBERBAND -#include <soundtouch/SoundTouch.h> -#endif - namespace ARDOUR { + class AudioRegion; +} -class AudioRegion; +#ifdef USE_RUBBERBAND -struct TimeStretchRequest : public InterThreadInfo { - float fraction; - bool quick_seek; - bool antialias; +#include <ardour/rb_effect.h> + +namespace ARDOUR { + +class Stretch : public RBEffect { + public: + Stretch (ARDOUR::Session&, TimeFXRequest&); + ~Stretch() {} }; +} /* namespace */ + +#else + +#include <soundtouch/SoundTouch.h> + +namespace ARDOUR { + class Stretch : public AudioFilter { public: - Stretch (ARDOUR::Session&, TimeStretchRequest&); + Stretch (ARDOUR::Session&, TimeFXRequest&); ~Stretch (); int run (boost::shared_ptr<ARDOUR::AudioRegion>); private: - TimeStretchRequest& tsr; + TimeFXRequest& tsr; -#ifndef USE_RUBBERBAND soundtouch::SoundTouch st; -#endif - }; } /* namespace */ +#endif + #endif /* __ardour_stretch_h__ */ diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index a8c6414798..2426b223dc 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -369,6 +369,13 @@ namespace ARDOUR { SrcFastest }; + struct TimeFXRequest : public InterThreadInfo { + float time_fraction; + float pitch_fraction; + bool quick_seek; + bool antialias; + }; + } // namespace ARDOUR std::istream& operator>>(std::istream& o, ARDOUR::SampleFormat& sf); diff --git a/libs/ardour/rb_pitch.cc b/libs/ardour/rb_effect.cc index e3c90346b8..ccd8317c71 100644 --- a/libs/ardour/rb_pitch.cc +++ b/libs/ardour/rb_effect.cc @@ -24,6 +24,7 @@ #include <rubberband/RubberBandStretcher.h> #include <ardour/types.h> +#include <ardour/stretch.h> #include <ardour/pitch.h> #include <ardour/audiofilesource.h> #include <ardour/session.h> @@ -37,6 +38,16 @@ using namespace PBD; using namespace RubberBand; Pitch::Pitch (Session& s, TimeFXRequest& req) + : RBEffect (s, req) +{ +} + +Stretch::Stretch (Session& s, TimeFXRequest& req) + : RBEffect (s, req) +{ +} + +RBEffect::RBEffect (Session& s, TimeFXRequest& req) : AudioFilter (s) , tsr (req) @@ -44,12 +55,12 @@ Pitch::Pitch (Session& s, TimeFXRequest& req) tsr.progress = 0.0f; } -Pitch::~Pitch () +RBEffect::~RBEffect () { } int -Pitch::run (boost::shared_ptr<AudioRegion> region) +RBEffect::run (boost::shared_ptr<AudioRegion> region) { SourceList nsrcs; nframes_t done; @@ -63,12 +74,12 @@ Pitch::run (boost::shared_ptr<AudioRegion> region) nframes_t pos = 0; int avail = 0; - RubberBandStretcher pitcher (session.frame_rate(), region->n_channels(), - RubberBandStretcher::DefaultOptions, - 1.0, tsr.fraction); + RubberBandStretcher stretcher (session.frame_rate(), region->n_channels(), + RubberBandStretcher::DefaultOptions, + tsr.time_fraction, tsr.pitch_fraction); - pitcher.setExpectedInputDuration(region->length()); - pitcher.setDebugLevel(1); + stretcher.setExpectedInputDuration(region->length()); + stretcher.setDebugLevel(1); tsr.progress = 0.0f; tsr.done = false; @@ -77,10 +88,18 @@ Pitch::run (boost::shared_ptr<AudioRegion> region) nframes_t duration = region->length(); /* the name doesn't need to be super-precise, but allow for 2 fractional - digits just to disambiguate close but not identical stretches. + digits just to disambiguate close but not identical FX */ - - snprintf (suffix, sizeof (suffix), "@%d", (int) floor (tsr.fraction * 100.0f)); + + if (tsr.time_fraction == 1.0) { + snprintf (suffix, sizeof (suffix), "@%d", (int) floor (tsr.pitch_fraction * 100.0f)); + } else if (tsr.pitch_fraction == 1.0) { + snprintf (suffix, sizeof (suffix), "@%d", (int) floor (tsr.time_fraction * 100.0f)); + } else { + snprintf (suffix, sizeof (suffix), "@%d-%d", + (int) floor (tsr.time_fraction * 100.0f), + (int) floor (tsr.pitch_fraction * 100.0f)); + } /* create new sources */ @@ -138,7 +157,7 @@ Pitch::run (boost::shared_ptr<AudioRegion> region) tsr.progress = ((float) done / duration) * 0.75; - pitcher.study(buffers, this_read, pos == duration); + stretcher.study(buffers, this_read, pos == duration); } done = 0; @@ -176,15 +195,15 @@ Pitch::run (boost::shared_ptr<AudioRegion> region) tsr.progress = 0.75 + ((float) done / duration) * 0.25; - pitcher.process(buffers, this_read, pos == duration); + stretcher.process(buffers, this_read, pos == duration); int avail = 0; - while ((avail = pitcher.available()) > 0) { + while ((avail = stretcher.available()) > 0) { this_read = min(bufsize, uint32_t(avail)); - pitcher.retrieve(buffers, this_read); + stretcher.retrieve(buffers, this_read); for (uint32_t i = 0; i < nsrcs.size(); ++i) { @@ -197,11 +216,11 @@ Pitch::run (boost::shared_ptr<AudioRegion> region) } } - while ((avail = pitcher.available()) >= 0) { + while ((avail = stretcher.available()) >= 0) { uint32_t this_read = min(bufsize, uint32_t(avail)); - pitcher.retrieve(buffers, this_read); + stretcher.retrieve(buffers, this_read); for (uint32_t i = 0; i < nsrcs.size(); ++i) { @@ -240,9 +259,17 @@ Pitch::run (boost::shared_ptr<AudioRegion> region) nframes_t start; nframes_t length; - float shift = (*x)->shift() * (tsr.fraction/100.0); + // note: tsr.time_fraction is a percentage of original length. 100 = no change, + // 50 is half as long, 200 is twice as long, etc. + + + float stretch = (*x)->stretch() * (tsr.time_fraction/100.0); + float shift = (*x)->shift() * tsr.pitch_fraction; + + start = (nframes_t) floor (astart + ((astart - (*x)->start()) / stretch)); + length = (nframes_t) floor (alength / stretch); - (*x)->set_ancestral_data ((*x)->ancestral_start(), (*x)->ancestral_length(), (*x)->stretch(), shift); + (*x)->set_ancestral_data (start, length, stretch, shift); } out: @@ -268,3 +295,8 @@ Pitch::run (boost::shared_ptr<AudioRegion> region) return ret; } + + + + + diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index e824eebaf0..8705228d1c 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -65,6 +65,7 @@ Region::Region (nframes_t start, nframes_t length, const string& name, layer_t l _ancestral_start = start; _ancestral_length = length; _stretch = 1.0; + _shift = 1.0; _last_position = 0; _position = 0; _layer = layer; @@ -92,6 +93,7 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes _ancestral_start = other->_ancestral_start + offset; _ancestral_length = length; _stretch = 1.0; + _shift = 1.0; _name = name; _last_position = 0; _position = 0; @@ -125,6 +127,7 @@ Region::Region (boost::shared_ptr<const Region> other) _ancestral_start = _start; _ancestral_length = _length; _stretch = 1.0; + _shift = 1.0; _name = other->_name; _last_position = other->_position; _position = other->_position; @@ -365,11 +368,12 @@ Region::nudge_position (nframes64_t n, void *src) } void -Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st) +Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh) { _ancestral_length = l; _ancestral_start = s; _stretch = st; + _shift = sh; } void @@ -795,6 +799,8 @@ Region::state (bool full_state) node->add_property ("ancestral-length", buf); snprintf (buf, sizeof (buf), "%.12g", _stretch); node->add_property ("stretch", buf); + snprintf (buf, sizeof (buf), "%.12g", _shift); + node->add_property ("shift", buf); switch (_first_edit) { case EditChangesNothing: @@ -924,6 +930,12 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send) _stretch = 1.0; } + if ((prop = node.property ("shift")) != 0) { + _shift = atof (prop->value()); + } else { + _shift = 1.0; + } + /* note: derived classes set flags */ if (_extra_xml) { diff --git a/libs/ardour/st_pitch.cc b/libs/ardour/st_pitch.cc new file mode 100644 index 0000000000..8626d0f619 --- /dev/null +++ b/libs/ardour/st_pitch.cc @@ -0,0 +1,56 @@ +/* + Copyright (C) 2004-2007 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <algorithm> +#include <cmath> + +#include <pbd/error.h> + +#include <ardour/types.h> +#include <ardour/pitch.h> +#include <ardour/audiofilesource.h> +#include <ardour/session.h> +#include <ardour/audioregion.h> + +#include "i18n.h" + +using namespace std; +using namespace ARDOUR; +using namespace PBD; + +Pitch::Pitch (Session& s, TimeFXRequest& req) + : AudioFilter (s) + , tsr (req) + +{ + tsr.progress = 0.0f; +} + +Pitch::~Pitch () +{ +} + +int +Pitch::run (boost::shared_ptr<AudioRegion> region) +{ + tsr.progress = 1.0f; + tsr.done = true; + + return 1; +} diff --git a/libs/ardour/st_stretch.cc b/libs/ardour/st_stretch.cc index 4cabfa79df..3c804e5549 100644 --- a/libs/ardour/st_stretch.cc +++ b/libs/ardour/st_stretch.cc @@ -35,7 +35,7 @@ using namespace ARDOUR; using namespace PBD; using namespace soundtouch; -Stretch::Stretch (Session& s, TimeStretchRequest& req) +Stretch::Stretch (Session& s, TimeFXRequest& req) : AudioFilter (s) , tsr (req) { @@ -45,7 +45,7 @@ Stretch::Stretch (Session& s, TimeStretchRequest& req) of opposite sign to the length change. */ - percentage = -tsr.fraction; + percentage = -tsr.time_fraction; st.setSampleRate (s.frame_rate()); st.setChannels (1); @@ -185,7 +185,7 @@ Stretch::run (boost::shared_ptr<AudioRegion> region) start = (nframes_t) floor (astart + ((astart - (*x)->start()) / stretch)); length = (nframes_t) floor (alength / stretch); - (*x)->set_ancestral_data (start, length, stretch); + (*x)->set_ancestral_data (start, length, stretch, (*x)->shift()); } out: |