diff --git a/SConstruct b/SConstruct
index 91e0bc0099..e2b34af5cd 100644
--- a/SConstruct
+++ b/SConstruct
@@ -64,6 +64,7 @@ class LibraryInfo(Environment):
self.Append (LIBPATH = other.get ('LIBPATH', []))
self.Append (CPPPATH = other.get('CPPPATH', []))
self.Append (LINKFLAGS = other.get('LINKFLAGS', []))
+ self.Append (CCFLAGS = other.get('CCFLAGS', []))
self.Replace(LIBPATH = list(Set(self.get('LIBPATH', []))))
self.Replace(CPPPATH = list(Set(self.get('CPPPATH',[]))))
#doing LINKFLAGS breaks -framework
@@ -434,6 +435,12 @@ def CheckPKGVersion(context, name, version):
context.Result( ret )
return ret
+def CheckPKGExists(context, name):
+ context.Message ('Checking for %s...' % name)
+ ret = context.TryAction('pkg-config --exists %s' % name)[0]
+ context.Result (ret)
+ return ret
+
conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig,
'CheckPKGVersion' : CheckPKGVersion })
@@ -472,27 +479,21 @@ libraries['raptor'].ParseConfig('pkg-config --cflags --libs raptor')
libraries['samplerate'] = LibraryInfo()
libraries['samplerate'].ParseConfig('pkg-config --cflags --libs samplerate')
-libraries['rubberband'] = LibraryInfo()
-#
-# chris cannam's rubberband has not yet been released
-#
-if os.path.exists ('libs/rubberband'):
- libraries['rubberband'] = LibraryInfo (LIBS='rubberband',
- LIBPATH='#libs/rubberband/lib',
- CPPPATH='#libs/rubberband/src',
- CXXFLAGS='-DUSE_RUBBERBAND')
+libraries['fftw3f'] = LibraryInfo()
+libraries['fftw3f'].ParseConfig('pkg-config --cflags --libs fftw3f')
+
+libraries['fftw3'] = LibraryInfo()
+libraries['fftw3'].ParseConfig('pkg-config --cflags --libs fftw3')
if env['FFT_ANALYSIS']:
- libraries['fftw3f'] = LibraryInfo()
- libraries['fftw3f'].ParseConfig('pkg-config --cflags --libs fftw3f')
#
# Check for fftw3 header as well as the library
- conf = Configure (libraries['fftw3f'])
+ #
+ conf = env.Configure()
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();
-
+ print ('FFT Analysis cannot be compiled without the FFTW3 headers, which do not seem to be installed')
+ sys.exit (1)
+
libraries['jack'] = LibraryInfo()
libraries['jack'].ParseConfig('pkg-config --cflags --libs jack')
@@ -732,6 +733,35 @@ def prep_libcheck(topenv, libinfo):
prep_libcheck(env, env)
+#
+# check for VAMP and rubberband (currently optional)
+#
+
+libraries['vamp'] = LibraryInfo()
+
+env['RUBBERBAND'] = False
+
+conf = Configure (libraries['vamp'], custom_tests = { 'CheckPKGExists' : CheckPKGExists } )
+
+if conf.CheckPKGExists('vamp-sdk'):
+ have_vamp = True
+ libraries['vamp'].ParseConfig('pkg-config --cflags --libs vamp-sdk')
+else:
+ have_vamp = False
+
+libraries['vamp'] = conf.Finish ()
+
+if have_vamp:
+ if os.path.exists ('libs/rubberband/src'):
+ conf = Configure (libraries['vamp'])
+ if conf.CheckHeader ('fftw3.h'):
+ env['RUBBERBAND'] = True
+ libraries['rubberband'] = LibraryInfo (LIBS='rubberband',
+ LIBPATH='#libs/rubberband',
+ CPPPATH='#libs/rubberband/rubberband',
+ CCFLAGS='-DUSE_RUBBERBAND')
+ libraries['vamp'] = conf.Finish ()
+
#
# Check for libusb
@@ -972,7 +1002,6 @@ else:
CPPPATH='#libs/appleutility')
coredirs = [
- 'libs/soundtouch',
'templates'
]
@@ -1038,6 +1067,14 @@ if env['SURFACES']:
else:
env['POWERMATE'] = 0
env['TRANZPORT'] = 0
+
+#
+# timestretch libraries
+#
+
+timefx_subdirs = ['libs/soundtouch']
+if env['RUBBERBAND']:
+ timefx_subdirs += ['libs/rubberband']
opts.Save('scache.conf', env)
Help(opts.GenerateHelpText(env))
@@ -1212,7 +1249,7 @@ env.AddPostAction (srcdist, Action ('rm -rf ' + str (File (env['DISTTREE']))))
for subdir in coredirs:
SConscript (subdir + '/SConscript')
-for sublistdir in [ subdirs, gtk_subdirs, surface_subdirs ]:
+for sublistdir in [ subdirs, timefx_subdirs, gtk_subdirs, surface_subdirs ]:
for subdir in sublistdir:
SConscript (subdir + '/SConscript')
diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript
index d7da86949c..0983fc81f8 100644
--- a/gtk2_ardour/SConscript
+++ b/gtk2_ardour/SConscript
@@ -49,8 +49,6 @@ gtkardour.Merge ([
libraries['gtk2'],
libraries['xml'],
libraries['xslt'],
- libraries['soundtouch'],
- libraries['rubberband'],
libraries['samplerate'],
libraries['jack']
])
@@ -76,6 +74,11 @@ if gtkardour['FFT_ANALYSIS']:
gtkardour.Merge ([libraries['fftw3f']])
gtkardour.Append(CCFLAGS='-DFFT_ANALYSIS')
+if gtkardour['RUBBERBAND']:
+ gtkardour.Merge ([ libraries['rubberband'], libraries['vamp'], libraries['fftw3f'], libraries['fftw3'] ])
+else:
+ gtkardour.Merge ([ libraries['soundtouch'] ])
+
skipped_files=Split("""
connection_editor.cc
""")
diff --git a/gtk2_ardour/ardev_common.sh.in b/gtk2_ardour/ardev_common.sh.in
index 6017dcdba4..7f40b1ec35 100644
--- a/gtk2_ardour/ardev_common.sh.in
+++ b/gtk2_ardour/ardev_common.sh.in
@@ -6,7 +6,7 @@ export ARDOUR_PATH=gtk2_ardour/icons:gtk2_ardour/pixmaps:gtk2_ardour
export GTK_PATH=libs/clearlooks
-export LD_LIBRARY_PATH=libs/surfaces/control_protocol:libs/ardour:libs/midi++2:libs/pbd:libs/soundtouch:libs/gtkmm2ext:libs/sigc++2:libs/glibmm2:libs/gtkmm2/atk:libs/gtkmm2/pango:libs/gtkmm2/gdk:libs/gtkmm2/gtk:libs/libgnomecanvasmm:libs/libsndfile:libs/appleutility:$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH=libs/surfaces/control_protocol:libs/ardour:libs/midi++2:libs/pbd:libs/rubberband:libs/soundtouch:libs/gtkmm2ext:libs/sigc++2:libs/glibmm2:libs/gtkmm2/atk:libs/gtkmm2/pango:libs/gtkmm2/gdk:libs/gtkmm2/gtk:libs/libgnomecanvasmm:libs/libsndfile:libs/appleutility:$LD_LIBRARY_PATH
# DYLD_LIBRARY_PATH is for darwin.
export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH
diff --git a/gtk2_ardour/ardour.menus b/gtk2_ardour/ardour.menus
index 4aa97b8996..288bf054d6 100644
--- a/gtk2_ardour/ardour.menus
+++ b/gtk2_ardour/ardour.menus
@@ -134,6 +134,8 @@
+
+
diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h
index deb8ea6106..7117ae9da2 100644
--- a/gtk2_ardour/editor.h
+++ b/gtk2_ardour/editor.h
@@ -45,8 +45,8 @@
#include
#include
-#include
#include
+#include
#include
#include
diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc
index 0a3f6a9134..ce1dc20ff0 100644
--- a/gtk2_ardour/editor_mouse.cc
+++ b/gtk2_ardour/editor_mouse.cc
@@ -5048,8 +5048,12 @@ Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event)
}
nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position();
+#ifdef USE_RUBBERBAND
+ float percentage = (float) ((double) newlen / (double) clicked_regionview->region()->length());
+#else
float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
-
+#endif
+
begin_reversible_command (_("timestretch"));
if (run_timestretch (selection->regions, percentage) == 0) {
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript
index 224fcc8656..d29f4a0b10 100644
--- a/libs/ardour/SConscript
+++ b/libs/ardour/SConscript
@@ -92,7 +92,6 @@ sndfile_helpers.cc
sndfilesource.cc
source.cc
source_factory.cc
-stretch.cc
tempo.cc
utils.cc
version.cc
@@ -106,6 +105,7 @@ vst_files = [ 'vst_plugin.cc', 'session_vst.cc' ]
audiounit_files = [ 'audio_unit.cc' ]
coreaudio_files = [ 'coreaudiosource.cc' ]
extra_sources = [ ]
+timefx_sources = [ ]
if ardour['VST']:
extra_sources += vst_files
@@ -260,22 +260,28 @@ ardour.Merge ([
libraries['samplerate'],
libraries['sigc2'],
libraries['pbd'],
- libraries['soundtouch'],
libraries['midi++2'],
libraries['glib2'],
libraries['glibmm2']
])
+if ardour['RUBBERBAND']:
+ ardour.Merge ([ libraries['rubberband'], libraries['vamp'], libraries['fftw3f'] ])
+ timefx_sources += [ 'rb_stretch.cc' ]
+else:
+ ardour.Merge ([ libraries['soundtouch'] ])
+ timefx_sources += [ 'st_stretch.cc' ]
+
if ardour['LIBLO']:
- ardour.Merge ([ libraries['lo'] ])
+ ardour.Merge ([ libraries['lo'] ])
if ardour['COREAUDIO'] or ardour['AUDIOUNITS']:
- ardour.Merge ([ libraries['appleutility'] ])
+ ardour.Merge ([ libraries['appleutility'] ])
def SharedAsmObjectEmitter(target, source, env):
- for tgt in target:
- tgt.attributes.shared = 1
- return (target, source)
+ for tgt in target:
+ tgt.attributes.shared = 1
+ return (target, source)
env['BUILDERS']['SharedAsmObject'] = Builder (action = '$CXX -c -fPIC $SOURCE -o $TARGET',
@@ -305,12 +311,12 @@ if env['FPU_OPTIMIZATION']:
arch_specific_objects = env.SharedAsmObject('sse_functions_64bit.os', 'sse_functions_64bit.s')
always_sse_objects += [ sse_env.SharedObject (source = 'sse_functions_xmm.cc') ]
-libardour = ardour.SharedLibrary('ardour', ardour_files + always_sse_objects + extra_sources + arch_specific_objects)
+libardour = ardour.SharedLibrary('ardour', ardour_files + always_sse_objects + timefx_sources + extra_sources + arch_specific_objects)
Default(libardour)
if env['NLS']:
- i18n (ardour, ardour_files + vst_files + coreaudio_files + audiounit_files, env)
+ i18n (ardour, ardour_files + vst_files + coreaudio_files + timefx_sources + audiounit_files, env)
env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libardour))
@@ -318,6 +324,8 @@ env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ar
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' ] +
+ [ 'SConscript', 'i18n.h', 'gettext.h' ] +
+ [ 'sse_functions_xmm.cc', 'sse_functions.s', 'sse_functions_64bit.s' ] +
+ [ 'rb_stretch.cc', 'st_stretch.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/stretch.h b/libs/ardour/ardour/stretch.h
index 02cc930ee5..6b5c2957e1 100644
--- a/libs/ardour/ardour/stretch.h
+++ b/libs/ardour/ardour/stretch.h
@@ -21,14 +21,17 @@
#define __ardour_stretch_h__
#include
+
+#ifndef USE_RUBBERBAND
#include
+#endif
namespace ARDOUR {
class AudioRegion;
struct TimeStretchRequest : public InterThreadInfo {
- float fraction;
+ float fraction;
bool quick_seek;
bool antialias;
};
@@ -42,7 +45,10 @@ class Stretch : public AudioFilter {
private:
TimeStretchRequest& tsr;
+
+#ifndef USE_RUBBERBAND
soundtouch::SoundTouch st;
+#endif
};
diff --git a/libs/ardour/rb_stretch.cc b/libs/ardour/rb_stretch.cc
new file mode 100644
index 0000000000..39d5816a5a
--- /dev/null
+++ b/libs/ardour/rb_stretch.cc
@@ -0,0 +1,276 @@
+/*
+ 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
+#include
+
+#include
+#include "/usr/local/music/src/rubberband/rubberband/RubberBandStretcher.h" //!!!
+
+#include
+#include
+#include
+#include
+#include
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+using namespace RubberBand;
+
+Stretch::Stretch (Session& s, TimeStretchRequest& req)
+ : AudioFilter (s)
+ , tsr (req)
+
+{
+ tsr.progress = 0.0f;
+}
+
+Stretch::~Stretch ()
+{
+}
+
+int
+Stretch::run (boost::shared_ptr region)
+{
+ SourceList nsrcs;
+ nframes_t done;
+ int ret = -1;
+ const nframes_t bufsize = 256;
+ gain_t* gain_buffer = 0;
+ Sample** buffers = 0;
+ char suffix[32];
+ string new_name;
+ string::size_type at;
+ nframes_t pos = 0;
+ int avail = 0;
+
+ RubberBandStretcher stretcher (session.frame_rate(), region->n_channels(),
+ RubberBandStretcher::DefaultOptions,
+ tsr.fraction, 1.0);
+
+ stretcher.setExpectedInputDuration(region->length());
+ stretcher.setDebugLevel(1);
+
+ tsr.progress = 0.0f;
+ tsr.done = false;
+
+ uint32_t channels = region->n_channels();
+ 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.
+ */
+
+ snprintf (suffix, sizeof (suffix), "@%d", (int) floor (tsr.fraction * 100.0f));
+
+ /* create new sources */
+
+ if (make_new_sources (region, nsrcs, suffix)) {
+ goto out;
+ }
+
+ gain_buffer = new gain_t[bufsize];
+ buffers = new float *[channels];
+
+ for (uint32_t i = 0; i < channels; ++i) {
+ buffers[i] = new float[bufsize];
+ }
+
+ /* 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. */
+
+ /* study first, process afterwards. */
+
+ pos = 0;
+ avail = 0;
+ done = 0;
+
+ try {
+ while (pos < duration && !tsr.cancel) {
+
+ nframes_t this_read = 0;
+
+ for (uint32_t i = 0; i < channels; ++i) {
+
+ this_read = 0;
+ nframes_t this_time;
+
+ this_time = min(bufsize, duration - pos);
+
+ this_read = region->master_read_at
+ (buffers[i],
+ buffers[i],
+ gain_buffer,
+ pos + region->position(),
+ this_time,
+ i);
+
+ if (this_read != this_time) {
+ error << string_compose
+ (_("tempoize: error reading data from %1"),
+ nsrcs[i]->name()) << endmsg;
+ goto out;
+ }
+ }
+
+ pos += this_read;
+ done += this_read;
+
+ tsr.progress = ((float) done / duration) * 0.25;
+
+ stretcher.study(buffers, this_read, pos == duration);
+ }
+
+ pos = 0;
+ tsr.done = 0;
+
+ while (pos < duration && !tsr.cancel) {
+
+ nframes_t this_read = 0;
+
+ for (uint32_t i = 0; i < channels; ++i) {
+
+ this_read = 0;
+ nframes_t this_time;
+
+ this_time = min(bufsize, duration - pos);
+
+ this_read = region->master_read_at
+ (buffers[i],
+ buffers[i],
+ gain_buffer,
+ pos + region->position(),
+ this_time,
+ i);
+
+ if (this_read != this_time) {
+ error << string_compose
+ (_("tempoize: error reading data from %1"),
+ nsrcs[i]->name()) << endmsg;
+ goto out;
+ }
+ }
+
+ pos += this_read;
+ done += this_read;
+
+ tsr.progress = 0.25 + ((float) done / duration) * 0.75;
+
+ 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 < nsrcs.size(); ++i) {
+
+ if (nsrcs[i]->write(buffers[i], this_read) !=
+ this_read) {
+ error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << 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 < nsrcs.size(); ++i) {
+
+ if (nsrcs[i]->write(buffers[i], this_read) !=
+ this_read) {
+ error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg;
+ goto out;
+ }
+ }
+ }
+
+ } catch (runtime_error& err) {
+ error << _("timefx code failure. please notify ardour-developers.") << endmsg;
+ error << err.what() << endmsg;
+ goto out;
+ }
+
+ new_name = region->name();
+ at = new_name.find ('@');
+
+ // remove any existing stretch indicator
+
+ if (at != string::npos && at > 2) {
+ new_name = new_name.substr (0, at - 1);
+ }
+
+ new_name += suffix;
+
+ ret = finish (region, nsrcs, new_name);
+
+ /* now reset ancestral data for each new region */
+
+ for (vector >::iterator x = results.begin(); x != results.end(); ++x) {
+ nframes64_t astart = (*x)->ancestral_start();
+ nframes64_t alength = (*x)->ancestral_length();
+ nframes_t start;
+ nframes_t length;
+
+ // note: tsr.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.fraction/100.0);
+
+ start = (nframes_t) floor (astart + ((astart - (*x)->start()) / stretch));
+ length = (nframes_t) floor (alength / stretch);
+
+ (*x)->set_ancestral_data (start, length, stretch);
+ }
+
+ out:
+
+ if (gain_buffer) {
+ delete [] gain_buffer;
+ }
+
+ if (buffers) {
+ for (uint32_t i = 0; i < channels; ++i) {
+ delete buffers[i];
+ }
+ delete [] buffers;
+ }
+
+ if (ret || tsr.cancel) {
+ for (SourceList::iterator si = nsrcs.begin(); si != nsrcs.end(); ++si) {
+ (*si)->mark_for_remove ();
+ }
+ }
+
+ tsr.done = true;
+
+ return ret;
+}
diff --git a/libs/ardour/stretch.cc b/libs/ardour/st_stretch.cc
similarity index 100%
rename from libs/ardour/stretch.cc
rename to libs/ardour/st_stretch.cc
diff --git a/libs/rubberband/SConscript b/libs/rubberband/SConscript
new file mode 100644
index 0000000000..db5c4cd3f6
--- /dev/null
+++ b/libs/rubberband/SConscript
@@ -0,0 +1,28 @@
+# -*- python -*-
+
+import os
+import os.path
+import glob
+
+rubberband_files = glob.glob ('src/*.cpp')
+
+Import('env install_prefix libraries')
+rb = env.Copy()
+
+rb.Merge ([libraries['fftw3f'],
+ libraries['vamp'],
+ libraries['sndfile-ardour']
+ ])
+
+rb.Append (CPPATH='#libs/rubberband/rubberband', CXXFLAGS="-Ilibs/rubberband/rubberband")
+
+librb = rb.SharedLibrary('rubberband', rubberband_files)
+
+Default(librb)
+
+env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), librb))
+
+env.Alias('tarball', env.Distribute (env['DISTTREE'],
+ [ 'SConscript'] + rubberband_files + glob.glob('src/*.h')))
+
+