mirror of
https://github.com/Ardour/ardour.git
synced 2026-01-09 15:15:41 +01:00
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:
parent
f99b844898
commit
fac1a37764
19 changed files with 426 additions and 129 deletions
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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'/>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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", ¤t_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", ¤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 (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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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')))
|
||||
|
|
|
|||
62
libs/ardour/ardour/pitch.h
Normal file
62
libs/ardour/ardour/pitch.h
Normal 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__ */
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
56
libs/ardour/st_pitch.cc
Normal 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;
|
||||
}
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue