Index: gtk2_ardour/editor_timefx.cc =================================================================== --- gtk2_ardour/editor_timefx.cc (revision 2499) +++ gtk2_ardour/editor_timefx.cc (working copy) @@ -106,7 +106,7 @@ } int -Editor::run_timestretch (RegionSelection& regions, float fraction) +Editor::run_timestretch (RegionSelection& regions, double ratio) { pthread_t thread; @@ -126,7 +126,7 @@ current_timestretch->status = 0; current_timestretch->regions = regions; - current_timestretch->request.fraction = fraction; + current_timestretch->request.ratio = ratio; 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; Index: gtk2_ardour/SConscript =================================================================== --- gtk2_ardour/SConscript (revision 2499) +++ gtk2_ardour/SConscript (working copy) @@ -23,6 +23,7 @@ #gtkardour.Append(CXXFLAGS="-DFLOWCANVAS_AA") gtkardour.Append(PACKAGE=domain) gtkardour.Append(POTFILE=domain + '.pot') +gtkardour.Append(LIBPATH="/work/rubberband");##!!! if gtkardour['DIST_TARGET'] == 'panther' or gtkardour['DIST_TARGET'] == 'tiger': gtkardour.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048") @@ -50,6 +51,9 @@ libraries['xml'], libraries['xslt'], libraries['soundtouch'], + libraries['rubberband'], + libraries['fftw3'], + libraries['fftw3f'], libraries['samplerate'], libraries['jack'] ]) Index: gtk2_ardour/editor_mouse.cc =================================================================== --- gtk2_ardour/editor_mouse.cc (revision 2499) +++ gtk2_ardour/editor_mouse.cc (working copy) @@ -4914,11 +4914,12 @@ } nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position(); - float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f; + double ratio = (double) newlen / + (double) clicked_regionview->region()->length(); begin_reversible_command (_("timestretch")); - if (run_timestretch (selection->regions, percentage) == 0) { + if (run_timestretch (selection->regions, ratio) == 0) { session->commit_reversible_command (); } } Index: gtk2_ardour/editor.h =================================================================== --- gtk2_ardour/editor.h (revision 2499) +++ gtk2_ardour/editor.h (working copy) @@ -1771,7 +1771,7 @@ TimeStretchDialog* current_timestretch; static void* timestretch_thread (void *arg); - int run_timestretch (RegionSelection&, float fraction); + int run_timestretch (RegionSelection&, double ratio); void do_timestretch (TimeStretchDialog&); /* editor-mixer strip */ Index: libs/ardour/session_timefx.cc =================================================================== --- libs/ardour/session_timefx.cc (revision 2499) +++ libs/ardour/session_timefx.cc (working copy) @@ -22,8 +22,10 @@ #include -#include +//#include +#include "/work/rubberband/rubberband/RubberBandStretcher.h" //!!! + #include #include #include @@ -35,7 +37,7 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -using namespace soundtouch; +using namespace RubberBand; boost::shared_ptr Session::tempoize_region (TimeStretchRequest& tsr) @@ -43,39 +45,44 @@ SourceList sources; SourceList::iterator it; boost::shared_ptr r; - SoundTouch st; string region_name; string ident = X_("-TIMEFX-"); - float percentage; - nframes_t total_frames; - nframes_t done; int c; char buf[64]; string::size_type len; - /* the soundtouch code wants a *tempo* change percentage, which is - of opposite sign to the length change. - */ + RubberBandStretcher stretcher + (frame_rate(), tsr.region->n_channels(), + RubberBandStretcher::DefaultOptions, + tsr.ratio, 1.0); + + stretcher.setExpectedInputDuration(tsr.region->length()); - percentage = -tsr.fraction; + stretcher.setDebugLevel(1); - st.setSampleRate (frame_rate()); - st.setChannels (1); - st.setTempoChange (percentage); - st.setPitchSemiTones (0); - st.setRateChange (0); - - st.setSetting(SETTING_USE_QUICKSEEK, tsr.quick_seek); - st.setSetting(SETTING_USE_AA_FILTER, tsr.antialias); + uint32_t channels = tsr.region->n_channels(); + nframes_t duration = tsr.region->length(); + nframes_t done; vector names = tsr.region->master_source_names(); tsr.progress = 0.0f; - total_frames = tsr.region->length() * tsr.region->n_channels(); done = 0; - for (uint32_t i = 0; i < tsr.region->n_channels(); ++i) { + const nframes_t bufsize = 256; //!!! was 16384 + float **buffers = new float *[channels]; + for (uint32_t i = 0; i < channels; ++i) { + buffers[i] = new float[bufsize]; + } + + gain_t gain_buffer[bufsize]; //!!! what is this? + + nframes_t pos = 0; + int avail = 0; + + for (uint32_t i = 0; i < channels; ++i) { + string rstr; string::size_type existing_ident; @@ -102,63 +109,127 @@ goto out; } } - - try { - const nframes_t bufsize = 16384; - for (uint32_t i = 0; i < sources.size(); ++i) { - gain_t gain_buffer[bufsize]; - Sample buffer[bufsize]; - nframes_t pos = 0; - nframes_t this_read = 0; + /* we read from the master (original) sources for the region, + not the ones currently in use, in case it's already been + subject to timefx. */ - st.clear(); - while (tsr.running && pos < tsr.region->length()) { - nframes_t this_time; - - this_time = min (bufsize, tsr.region->length() - pos); + /* study first, process afterwards. */ - /* read from the master (original) sources for the region, - not the ones currently in use, in case it's already been - subject to timefx. */ + while (pos < duration && tsr.running) { - if ((this_read = tsr.region->master_read_at (buffer, buffer, gain_buffer, pos + tsr.region->position(), this_time)) != this_time) { - error << string_compose (_("tempoize: error reading data from %1"), sources[i]->name()) << endmsg; - goto out; - } + nframes_t this_read = 0; + + for (uint32_t i = 0; i < sources.size(); ++i) { - pos += this_read; - done += this_read; + this_read = 0; + nframes_t this_time; - tsr.progress = (float) done / total_frames; + this_time = min(bufsize, duration - pos); + + this_read = tsr.region->master_read_at + (buffers[i], + buffers[i], + gain_buffer, + pos + tsr.region->position(), + this_time, + i); + +// std::cerr << "read " << this_read << " from " << pos << " (length=" << duration << ")" << std::endl; + + if (this_read != this_time) { + error << string_compose + (_("tempoize: error reading data from %1"), + sources[i]->name()) << endmsg; + goto out; + } + } + + pos += this_read; + done += this_read; + + tsr.progress = ((float) done / duration) * 0.25; - st.putSamples (buffer, this_read); + stretcher.study(buffers, this_read, pos == duration); + } + + pos = 0; + done = 0; + + while (pos < duration && tsr.running) { + + nframes_t this_read = 0; + + for (uint32_t i = 0; i < sources.size(); ++i) { - while ((this_read = st.receiveSamples (buffer, bufsize)) > 0 && tsr.running) { - if (sources[i]->write (buffer, this_read) != this_read) { - error << string_compose (_("error writing tempo-adjusted data to %1"), sources[i]->name()) << endmsg; - goto out; - } - } + this_read = 0; + nframes_t this_time; + + this_time = min(bufsize, duration - pos); + + this_read = tsr.region->master_read_at + (buffers[i], + buffers[i], + gain_buffer, + pos + tsr.region->position(), + this_time, + i); + + if (this_read != this_time) { + error << string_compose + (_("tempoize: error reading data from %1"), + sources[i]->name()) << endmsg; + goto out; } - - if (tsr.running) { - st.flush (); - } - - while (tsr.running && (this_read = st.receiveSamples (buffer, bufsize)) > 0) { - if (sources[i]->write (buffer, this_read) != this_read) { + } + + pos += this_read; + done += this_read; + +// std::cerr << "read " << this_read << " from " << pos << " (length=" << duration << ")" << std::endl; + + tsr.progress = 0.25 + ((float) done / duration) * 0.75; + +// std::cerr << "progress = " << tsr.progress << std::endl; + + stretcher.process(buffers, this_read, pos == duration); + + int avail = 0; + + while ((avail = stretcher.available()) > 0) { + + this_read = min(bufsize, uint32_t(avail)); + + stretcher.retrieve(buffers, this_read); + + for (uint32_t i = 0; i < sources.size(); ++i) { + + if (sources[i]->write(buffers[i], this_read) != + this_read) { error << string_compose (_("error writing tempo-adjusted data to %1"), sources[i]->name()) << endmsg; goto out; } +// std::cerr << "wrote " << this_read << " to channel " << i << std::endl; } } - } catch (runtime_error& err) { - error << _("timefx code failure. please notify ardour-developers.") << endmsg; - error << err.what() << endmsg; - goto out; } + while ((avail = stretcher.available()) >= 0) { + + uint32_t this_read = min(bufsize, uint32_t(avail)); + + stretcher.retrieve(buffers, this_read); + + for (uint32_t i = 0; i < sources.size(); ++i) { + + if (sources[i]->write(buffers[i], this_read) != + this_read) { + error << string_compose (_("error writing tempo-adjusted data to %1"), sources[i]->name()) << endmsg; + goto out; + } + } + } + time_t now; struct tm* xnow; time (&now); @@ -206,8 +277,11 @@ } - r = (boost::dynamic_pointer_cast (RegionFactory::create (sources, 0, sources.front()->length(), region_name, - 0, AudioRegion::Flag (AudioRegion::DefaultFlags | AudioRegion::WholeFile)))); + r = (boost::dynamic_pointer_cast + (RegionFactory::create + (sources, 0, sources.front()->length(), region_name, 0, + AudioRegion::Flag + (AudioRegion::DefaultFlags | AudioRegion::WholeFile)))); out: @@ -225,6 +299,13 @@ sources.clear (); } + + if (buffers) { + for (uint32_t i = 0; i < channels; ++i) { + delete[] buffers[i]; + } + delete[] buffers; + } /* if the process was cancelled, delete the region */ Index: libs/ardour/ardour/session.h =================================================================== --- libs/ardour/ardour/session.h (revision 2499) +++ libs/ardour/ardour/session.h (working copy) @@ -849,7 +849,7 @@ struct TimeStretchRequest { boost::shared_ptr region; - float fraction; /* session: read ; GUI: write */ + double ratio; /* session: read ; GUI: write */ float progress; /* session: write ; GUI: read */ bool running; /* read/write */ bool quick_seek; /* GUI: write */ Index: SConstruct =================================================================== --- SConstruct (revision 2499) +++ SConstruct (working copy) @@ -481,7 +481,16 @@ if conf.CheckHeader ('fftw3.h') == False: print "FFT Analysis cannot be compiled without the FFTW3 headers, which don't seem to be installed" sys.exit (1) - libraries['fftw3f'] = conf.Finish(); + libraries['fftw3f'] = conf.Finish() + libraries['fftw3'] = LibraryInfo() + libraries['fftw3'].ParseConfig('pkg-config --cflags --libs fftw3') + # + # Check for fftw3 header as well as the library + conf = Configure (libraries['fftw3']) + if conf.CheckHeader ('fftw3.h') == False: + print "FFT Analysis cannot be compiled without the FFTW3 headers, which don't seem to be installed" + sys.exit (1) + libraries['fftw3'] = conf.Finish(); libraries['jack'] = LibraryInfo() libraries['jack'].ParseConfig('pkg-config --cflags --libs jack') @@ -509,6 +518,9 @@ #libraries['flowcanvas'] = LibraryInfo(LIBS='flowcanvas', LIBPATH='#/libs/flowcanvas', CPPPATH='#libs/flowcanvas') +##!!! +libraries['rubberband'] = LibraryInfo (LIBS='rubberband', LIBPATH='/work/rubberband/lib') + # The Ardour Control Protocol Library libraries['ardour_cp'] = LibraryInfo (LIBS='ardour_cp', LIBPATH='#libs/surfaces/control_protocol',