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 (LIBPATH = other.get ('LIBPATH', []))
self.Append (CPPPATH = other.get('CPPPATH', [])) self.Append (CPPPATH = other.get('CPPPATH', []))
self.Append (LINKFLAGS = other.get('LINKFLAGS', [])) self.Append (LINKFLAGS = other.get('LINKFLAGS', []))
self.Append (CCFLAGS = other.get('CCFLAGS', []))
self.Replace(LIBPATH = list(Set(self.get('LIBPATH', [])))) self.Replace(LIBPATH = list(Set(self.get('LIBPATH', []))))
self.Replace(CPPPATH = list(Set(self.get('CPPPATH',[])))) self.Replace(CPPPATH = list(Set(self.get('CPPPATH',[]))))
#doing LINKFLAGS breaks -framework #doing LINKFLAGS breaks -framework
@ -434,6 +435,12 @@ def CheckPKGVersion(context, name, version):
context.Result( ret ) context.Result( ret )
return 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, conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig,
'CheckPKGVersion' : CheckPKGVersion }) 'CheckPKGVersion' : CheckPKGVersion })
@ -472,27 +479,21 @@ libraries['raptor'].ParseConfig('pkg-config --cflags --libs raptor')
libraries['samplerate'] = LibraryInfo() libraries['samplerate'] = LibraryInfo()
libraries['samplerate'].ParseConfig('pkg-config --cflags --libs samplerate') libraries['samplerate'].ParseConfig('pkg-config --cflags --libs samplerate')
libraries['rubberband'] = LibraryInfo() libraries['fftw3f'] = LibraryInfo()
# libraries['fftw3f'].ParseConfig('pkg-config --cflags --libs fftw3f')
# chris cannam's rubberband has not yet been released
# libraries['fftw3'] = LibraryInfo()
if os.path.exists ('libs/rubberband'): libraries['fftw3'].ParseConfig('pkg-config --cflags --libs fftw3')
libraries['rubberband'] = LibraryInfo (LIBS='rubberband',
LIBPATH='#libs/rubberband/lib',
CPPPATH='#libs/rubberband/src',
CXXFLAGS='-DUSE_RUBBERBAND')
if env['FFT_ANALYSIS']: if env['FFT_ANALYSIS']:
libraries['fftw3f'] = LibraryInfo()
libraries['fftw3f'].ParseConfig('pkg-config --cflags --libs fftw3f')
# #
# Check for fftw3 header as well as the library # Check for fftw3 header as well as the library
conf = Configure (libraries['fftw3f']) #
conf = env.Configure()
if conf.CheckHeader ('fftw3.h') == False: if conf.CheckHeader ('fftw3.h') == False:
print "FFT Analysis cannot be compiled without the FFTW3 headers, which don't seem to be installed" print ('FFT Analysis cannot be compiled without the FFTW3 headers, which do not seem to be installed')
sys.exit (1) sys.exit (1)
libraries['fftw3f'] = conf.Finish();
libraries['jack'] = LibraryInfo() libraries['jack'] = LibraryInfo()
libraries['jack'].ParseConfig('pkg-config --cflags --libs jack') libraries['jack'].ParseConfig('pkg-config --cflags --libs jack')
@ -732,6 +733,35 @@ def prep_libcheck(topenv, libinfo):
prep_libcheck(env, env) 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 # Check for libusb
@ -972,7 +1002,6 @@ else:
CPPPATH='#libs/appleutility') CPPPATH='#libs/appleutility')
coredirs = [ coredirs = [
'libs/soundtouch',
'templates' 'templates'
] ]
@ -1038,6 +1067,14 @@ if env['SURFACES']:
else: else:
env['POWERMATE'] = 0 env['POWERMATE'] = 0
env['TRANZPORT'] = 0 env['TRANZPORT'] = 0
#
# timestretch libraries
#
timefx_subdirs = ['libs/soundtouch']
if env['RUBBERBAND']:
timefx_subdirs += ['libs/rubberband']
opts.Save('scache.conf', env) opts.Save('scache.conf', env)
Help(opts.GenerateHelpText(env)) Help(opts.GenerateHelpText(env))
@ -1212,7 +1249,7 @@ env.AddPostAction (srcdist, Action ('rm -rf ' + str (File (env['DISTTREE']))))
for subdir in coredirs: for subdir in coredirs:
SConscript (subdir + '/SConscript') 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: for subdir in sublistdir:
SConscript (subdir + '/SConscript') SConscript (subdir + '/SConscript')

View file

@ -49,8 +49,6 @@ gtkardour.Merge ([
libraries['gtk2'], libraries['gtk2'],
libraries['xml'], libraries['xml'],
libraries['xslt'], libraries['xslt'],
libraries['soundtouch'],
libraries['rubberband'],
libraries['samplerate'], libraries['samplerate'],
libraries['jack'] libraries['jack']
]) ])
@ -76,6 +74,11 @@ if gtkardour['FFT_ANALYSIS']:
gtkardour.Merge ([libraries['fftw3f']]) gtkardour.Merge ([libraries['fftw3f']])
gtkardour.Append(CCFLAGS='-DFFT_ANALYSIS') 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(""" skipped_files=Split("""
connection_editor.cc 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 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. # DYLD_LIBRARY_PATH is for darwin.
export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH

View file

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

View file

@ -45,8 +45,8 @@
#include <pbd/stateful.h> #include <pbd/stateful.h>
#include <ardour/session.h> #include <ardour/session.h>
#include <ardour/stretch.h>
#include <ardour/tempo.h> #include <ardour/tempo.h>
#include <ardour/stretch.h>
#include <ardour/location.h> #include <ardour/location.h>
#include <ardour/audioregion.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(); 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; float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f;
#endif
begin_reversible_command (_("timestretch")); begin_reversible_command (_("timestretch"));
if (run_timestretch (selection->regions, percentage) == 0) { if (run_timestretch (selection->regions, percentage) == 0) {

View file

@ -92,7 +92,6 @@ sndfile_helpers.cc
sndfilesource.cc sndfilesource.cc
source.cc source.cc
source_factory.cc source_factory.cc
stretch.cc
tempo.cc tempo.cc
utils.cc utils.cc
version.cc version.cc
@ -106,6 +105,7 @@ vst_files = [ 'vst_plugin.cc', 'session_vst.cc' ]
audiounit_files = [ 'audio_unit.cc' ] audiounit_files = [ 'audio_unit.cc' ]
coreaudio_files = [ 'coreaudiosource.cc' ] coreaudio_files = [ 'coreaudiosource.cc' ]
extra_sources = [ ] extra_sources = [ ]
timefx_sources = [ ]
if ardour['VST']: if ardour['VST']:
extra_sources += vst_files extra_sources += vst_files
@ -260,22 +260,28 @@ ardour.Merge ([
libraries['samplerate'], libraries['samplerate'],
libraries['sigc2'], libraries['sigc2'],
libraries['pbd'], libraries['pbd'],
libraries['soundtouch'],
libraries['midi++2'], libraries['midi++2'],
libraries['glib2'], libraries['glib2'],
libraries['glibmm2'] 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']: if ardour['LIBLO']:
ardour.Merge ([ libraries['lo'] ]) ardour.Merge ([ libraries['lo'] ])
if ardour['COREAUDIO'] or ardour['AUDIOUNITS']: if ardour['COREAUDIO'] or ardour['AUDIOUNITS']:
ardour.Merge ([ libraries['appleutility'] ]) ardour.Merge ([ libraries['appleutility'] ])
def SharedAsmObjectEmitter(target, source, env): def SharedAsmObjectEmitter(target, source, env):
for tgt in target: for tgt in target:
tgt.attributes.shared = 1 tgt.attributes.shared = 1
return (target, source) return (target, source)
env['BUILDERS']['SharedAsmObject'] = Builder (action = '$CXX -c -fPIC $SOURCE -o $TARGET', 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') arch_specific_objects = env.SharedAsmObject('sse_functions_64bit.os', 'sse_functions_64bit.s')
always_sse_objects += [ sse_env.SharedObject (source = 'sse_functions_xmm.cc') ] 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) Default(libardour)
if env['NLS']: 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)) 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('version', ardour.VersionBuild(['version.cc', 'ardour/version.h'], []))
env.Alias('tarball', env.Distribute (env['DISTTREE'], 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 + ardour_files + osc_files + vst_files + coreaudio_files + audiounit_files +
glob.glob('po/*.po') + glob.glob('ardour/*.h'))) glob.glob('po/*.po') + glob.glob('ardour/*.h')))

View file

@ -21,14 +21,17 @@
#define __ardour_stretch_h__ #define __ardour_stretch_h__
#include <ardour/audiofilter.h> #include <ardour/audiofilter.h>
#ifndef USE_RUBBERBAND
#include <soundtouch/SoundTouch.h> #include <soundtouch/SoundTouch.h>
#endif
namespace ARDOUR { namespace ARDOUR {
class AudioRegion; class AudioRegion;
struct TimeStretchRequest : public InterThreadInfo { struct TimeStretchRequest : public InterThreadInfo {
float fraction; float fraction;
bool quick_seek; bool quick_seek;
bool antialias; bool antialias;
}; };
@ -42,7 +45,10 @@ class Stretch : public AudioFilter {
private: private:
TimeStretchRequest& tsr; TimeStretchRequest& tsr;
#ifndef USE_RUBBERBAND
soundtouch::SoundTouch st; 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')))