integrate the not-yet-released RubberBand library into ardour for timefx

git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2704 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2007-11-20 20:20:47 +00:00
parent e9e328b469
commit 678de81b96
11 changed files with 398 additions and 34 deletions

View file

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

View file

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

View file

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

View file

@ -134,6 +134,8 @@
<menuitem action='finish-add-range'/>
<separator/>
<menuitem action='select-all'/>
<menuitem action='deselect-all'/>
<menuitem action='invert-selection'/>
<menuitem action='select-all-after-edit-cursor'/>
<menuitem action='select-all-before-edit-cursor'/>
<menuitem action='select-all-after-playhead'/>

View file

@ -45,8 +45,8 @@
#include <pbd/stateful.h>
#include <ardour/session.h>
#include <ardour/stretch.h>
#include <ardour/tempo.h>
#include <ardour/stretch.h>
#include <ardour/location.h>
#include <ardour/audioregion.h>

View file

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

View file

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

View file

@ -21,14 +21,17 @@
#define __ardour_stretch_h__
#include <ardour/audiofilter.h>
#ifndef USE_RUBBERBAND
#include <soundtouch/SoundTouch.h>
#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
};

276
libs/ardour/rb_stretch.cc Normal file
View file

@ -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 <algorithm>
#include <cmath>
#include <pbd/error.h>
#include "/usr/local/music/src/rubberband/rubberband/RubberBandStretcher.h" //!!!
#include <ardour/types.h>
#include <ardour/stretch.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;
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<AudioRegion> 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<boost::shared_ptr<AudioRegion> >::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;
}

View file

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