#include <cstdlib>
#include <cmath>
#include <ctime>
-
#include <string>
+#include <set>
#include "pbd/error.h"
#include "pbd/pthread_utils.h"
#include "region_selection.h"
#include "time_fx_dialog.h"
-#include "ardour/session.h"
-#include "ardour/region.h"
-#include "ardour/audioplaylist.h"
-#include "ardour/audio_track.h"
#include "ardour/audioregion.h"
-#include "ardour/stretch.h"
#include "ardour/midi_stretch.h"
#include "ardour/pitch.h"
+#include "ardour/region.h"
+#include "ardour/session.h"
+#include "ardour/stretch.h"
#ifdef USE_RUBBERBAND
-#include "rubberband/RubberBandStretcher.h"
+#include <rubberband/RubberBandStretcher.h>
using namespace RubberBand;
#endif
int
Editor::time_stretch (RegionSelection& regions, float fraction)
{
- // FIXME: kludge, implement stretching of selection of both types
+ RegionList audio;
+ RegionList midi;
+ int aret;
- if (regions.front()->region()->data_type() == DataType::AUDIO) {
- // Audio, pop up timefx dialog
- return time_fx (regions, fraction, false);
- } else {
- // MIDI, just stretch
- RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (®ions.front()->get_time_axis_view());
- if (!rtv)
- return -1;
-
- boost::shared_ptr<Playlist> playlist = rtv->track()->playlist();
-
- ARDOUR::TimeFXRequest request;
- request.time_fraction = fraction;
- MidiStretch stretch(*_session, request);
- begin_reversible_command ("midi stretch");
- stretch.run(regions.front()->region());
- playlist->clear_changes ();
- playlist->replace_region (regions.front()->region(), stretch.results[0],
- regions.front()->region()->position());
- _session->add_command (new StatefulDiffCommand (playlist));
+ begin_reversible_command (_("stretch/shrink"));
+
+ for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->region()->data_type() == DataType::AUDIO) {
+ audio.push_back ((*i)->region());
+ } else if ((*i)->region()->data_type() == DataType::MIDI) {
+ midi.push_back ((*i)->region());
+ }
+ }
+
+ if ((aret = time_fx (audio, fraction, false)) != 0) {
commit_reversible_command ();
+ return aret;
}
+ set<boost::shared_ptr<Playlist> > midi_playlists_affected;
+
+ for (RegionList::iterator i = midi.begin(); i != midi.end(); ++i) {
+ boost::shared_ptr<Playlist> playlist = (*i)->playlist();
+
+ if (playlist) {
+ playlist->clear_changes ();
+ }
+
+ }
+
+ ARDOUR::TimeFXRequest request;
+ request.time_fraction = fraction;
+
+ for (RegionList::iterator i = midi.begin(); i != midi.end(); ++i) {
+ boost::shared_ptr<Playlist> playlist = (*i)->playlist();
+
+ if (!playlist) {
+ continue;
+ }
+
+ MidiStretch stretch (*_session, request);
+ stretch.run (*i);
+
+ playlist->replace_region (regions.front()->region(), stretch.results[0],
+ regions.front()->region()->position());
+ midi_playlists_affected.insert (playlist);
+ }
+
+ for (set<boost::shared_ptr<Playlist> >::iterator p = midi_playlists_affected.begin(); p != midi_playlists_affected.end(); ++p) {
+ _session->add_command (new StatefulDiffCommand (*p));
+ }
+
+ commit_reversible_command ();
+
return 0;
}
int
Editor::pitch_shift (RegionSelection& regions, float fraction)
{
- return time_fx (regions, fraction, true);
+ RegionList rl;
+
+ for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
+ rl.push_back ((*i)->region());
+ }
+
+ begin_reversible_command (_("pitch shift"));
+
+ int ret = time_fx (rl, fraction, true);
+
+ if (ret == 0) {
+ commit_reversible_command ();
+ } else {
+ abort_reversible_command ();
+ }
+
+ return ret;
}
/** @param val Percentage to time stretch by; ignored if pitch-shifting.
* @param pitching true to pitch shift, false to time stretch.
* @return -1 in case of error, 1 if operation was cancelled by the user, 0 if everything went ok */
int
-Editor::time_fx (RegionSelection& regions, float val, bool pitching)
+Editor::time_fx (RegionList& regions, float val, bool pitching)
{
delete current_timefx;
-
current_timefx = new TimeFXDialog (*this, pitching);
+ current_timefx->regions = regions;
- current_timefx->progress_bar.set_fraction (0.0f);
+ /* See if we have any audio regions on our list */
+ RegionList::iterator i = regions.begin ();
+ while (i != regions.end() && boost::dynamic_pointer_cast<AudioRegion> (*i) == 0) {
+ ++i;
+ }
+ if (i == regions.end ()) {
+ /* No audio regions; we can just do the timefx without a dialogue */
+ do_timefx ();
+ return 0;
+ }
+
switch (current_timefx->run ()) {
case RESPONSE_ACCEPT:
break;
}
current_timefx->status = 0;
- current_timefx->regions = regions;
if (pitching) {
current_timefx->request.quick_seek = current_timefx->quick_button.get_active();
current_timefx->request.antialias = !current_timefx->antialias_button.get_active();
#endif
- current_timefx->request.progress = 0.0f;
current_timefx->request.done = false;
current_timefx->request.cancel = false;
pthread_detach (current_timefx->request.thread);
- sigc::connection c = Glib::signal_timeout().connect (sigc::mem_fun (current_timefx, &TimeFXDialog::update_progress), 100);
-
while (!current_timefx->request.done && !current_timefx->request.cancel) {
gtk_main_iteration ();
}
- c.disconnect ();
+ pthread_join (current_timefx->request.thread, 0);
current_timefx->hide ();
return current_timefx->status;
}
void
-Editor::do_timefx (TimeFXDialog& dialog)
+Editor::do_timefx ()
{
- Track* t;
boost::shared_ptr<Playlist> playlist;
boost::shared_ptr<Region> new_region;
- bool in_command = false;
+ set<boost::shared_ptr<Playlist> > playlists_affected;
- for (RegionSelection::iterator i = dialog.regions.begin(); i != dialog.regions.end(); ) {
- AudioRegionView* arv = dynamic_cast<AudioRegionView*>(*i);
+ uint32_t const N = current_timefx->regions.size ();
- if (!arv) {
- continue;
- }
-
- boost::shared_ptr<AudioRegion> region (arv->audio_region());
- TimeAxisView* tv = &(arv->get_time_axis_view());
- RouteTimeAxisView* rtv;
- RegionSelection::iterator tmp;
-
- tmp = i;
- ++tmp;
+ for (RegionList::iterator i = current_timefx->regions.begin(); i != current_timefx->regions.end(); ++i) {
+ boost::shared_ptr<Playlist> playlist = (*i)->playlist();
- if ((rtv = dynamic_cast<RouteTimeAxisView*> (tv)) == 0) {
- i = tmp;
- continue;
+ if (playlist) {
+ playlist->clear_changes ();
}
+ }
- if ((t = dynamic_cast<Track*> (rtv->route().get())) == 0) {
- i = tmp;
- continue;
- }
+ for (RegionList::iterator i = current_timefx->regions.begin(); i != current_timefx->regions.end(); ++i) {
- if ((playlist = t->playlist()) == 0) {
- i = tmp;
+ boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
+
+ if (!region || (playlist = region->playlist()) == 0) {
continue;
}
- if (dialog.request.cancel) {
+ if (current_timefx->request.cancel) {
/* we were cancelled */
- dialog.status = 1;
+ /* XXX what to do about playlists already affected ? */
+ current_timefx->status = 1;
return;
}
Filter* fx;
- if (dialog.pitching) {
- fx = new Pitch (*_session, dialog.request);
+ if (current_timefx->pitching) {
+ fx = new Pitch (*_session, current_timefx->request);
} else {
#ifdef USE_RUBBERBAND
- fx = new RBStretch (*_session, dialog.request);
+ fx = new RBStretch (*_session, current_timefx->request);
#else
- fx = new STStretch (*_session, dialog.request);
+ fx = new STStretch (*_session, current_timefx->request);
#endif
}
- if (fx->run (region)) {
- dialog.status = -1;
- dialog.request.done = true;
+ current_timefx->descend (1.0 / N);
+
+ if (fx->run (region, current_timefx)) {
+ current_timefx->status = -1;
+ current_timefx->request.done = true;
delete fx;
return;
}
if (!fx->results.empty()) {
new_region = fx->results.front();
- if (!in_command) {
- _session->begin_reversible_command (dialog.pitching ? _("pitch shift") : _("time stretch"));
- in_command = true;
- }
-
- playlist->clear_changes ();
playlist->replace_region (region, new_region, region->position());
- _session->add_command (new StatefulDiffCommand (playlist));
+ playlists_affected.insert (playlist);
}
- i = tmp;
+ current_timefx->ascend ();
delete fx;
}
- if (in_command) {
- _session->commit_reversible_command ();
+ for (set<boost::shared_ptr<Playlist> >::iterator p = playlists_affected.begin(); p != playlists_affected.end(); ++p) {
+ _session->add_command (new StatefulDiffCommand (*p));
}
- dialog.status = 0;
- dialog.request.done = true;
+ current_timefx->status = 0;
+ current_timefx->request.done = true;
}
void*
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
- tsd->editor.do_timefx (*tsd);
+ tsd->editor.do_timefx ();
/* GACK! HACK! sleep for a bit so that our request buffer for the GUI
event loop doesn't die before any changes we made are processed
by the GUI ...
*/
+#ifdef PLATFORM_WINDOWS
+ Glib::usleep(2 * G_USEC_PER_SEC);
+#else
struct timespec t = { 2, 0 };
nanosleep (&t, 0);
-
+#endif
return 0;
}