add pitch shifting; minor fixes elsewhere

git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2740 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2007-12-04 20:17:28 +00:00
parent f99b844898
commit fac1a37764
19 changed files with 426 additions and 129 deletions

View file

@ -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")

View file

@ -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'/>

View file

@ -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;

View file

@ -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;

View file

@ -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 */

View file

@ -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);

View file

@ -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);
}

View file

@ -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 ();
}
}

View file

@ -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);
}

View file

@ -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());
get_vbox()->set_spacing (5);
get_vbox()->set_border_width (5);
get_vbox()->pack_start (upper_button_box);
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);
action_button = add_button (_("Stretch/Shrink it"), Gtk::RESPONSE_ACCEPT);
cancel_button = add_button (_("Cancel"), Gtk::RESPONSE_CANCEL);
quick_button.set_name (N_("TimeStretchButton"));
antialias_button.set_name (N_("TimeStretchButton"));
progress_bar.set_name (N_("TimeStretchProgress"));
get_vbox()->set_spacing (5);
get_vbox()->set_border_width (12);
get_vbox()->pack_start (upper_button_box, false, false);
get_vbox()->pack_start (progress_bar);
if (pitching) {
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);
} 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)
{
if (current_timestretch == 0) {
current_timestretch = new TimeStretchDialog (*this);
return time_fx (regions, fraction, false);
}
int
Editor::pitch_shift (RegionSelection& regions, float fraction)
{
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);
switch (current_timestretch->run ()) {
current_timefx->progress_bar.set_fraction (0.0f);
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));
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 ("timestretch", &current_timestretch->request.thread, 0, timestretch_thread, current_timestretch)) {
current_timestretch->hide ();
error << _("timestretch cannot be started - thread creation error") << endmsg;
if (pthread_create_and_store ("timefx", &current_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 (stretch.run (region)) {
if (dialog.pitching) {
fx = new Pitch (*session, dialog.request);
} else {
fx = new Stretch (*session, dialog.request);
}
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;
}

View file

@ -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')))

View file

@ -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__ */

View file

@ -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 */

View file

@ -22,36 +22,45 @@
#include <ardour/audiofilter.h>
#ifndef USE_RUBBERBAND
#include <soundtouch/SoundTouch.h>
#endif
namespace ARDOUR {
class AudioRegion;
}
#ifdef USE_RUBBERBAND
#include <ardour/rb_effect.h>
namespace ARDOUR {
class AudioRegion;
struct TimeStretchRequest : public InterThreadInfo {
float fraction;
bool quick_seek;
bool antialias;
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__ */

View file

@ -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);

View file

@ -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;
(*x)->set_ancestral_data ((*x)->ancestral_start(), (*x)->ancestral_length(), (*x)->stretch(), shift);
start = (nframes_t) floor (astart + ((astart - (*x)->start()) / stretch));
length = (nframes_t) floor (alength / stretch);
(*x)->set_ancestral_data (start, length, stretch, shift);
}
out:
@ -268,3 +295,8 @@ Pitch::run (boost::shared_ptr<AudioRegion> region)
return ret;
}

View file

@ -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) {

56
libs/ardour/st_pitch.cc Normal file
View file

@ -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;
}

View file

@ -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: