2007-11-06 21:41:16 +00:00
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
2012-09-09 16:57:42 +01:00
Rubber Band Library
2007-11-06 21:41:16 +00:00
An audio time - stretching and pitch - shifting library .
2012-09-09 16:57:42 +01:00
Copyright 2007 - 2012 Particular Programs Ltd .
2007-11-06 21:41:16 +00:00
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 . See the file
COPYING included with this distribution for more information .
2012-09-09 16:57:42 +01:00
Alternatively , if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders , you may redistribute and / or modify it under the terms
described in that licence .
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License ,
you must obtain a valid commercial licence before doing so .
2007-11-06 21:41:16 +00:00
*/
# include "StretcherImpl.h"
2009-09-17 13:01:21 +00:00
2012-09-09 16:57:42 +01:00
# include "audiocurves/PercussiveAudioCurve.h"
# include "audiocurves/HighFrequencyAudioCurve.h"
# include "audiocurves/SpectralDifferenceAudioCurve.h"
# include "audiocurves/SilentAudioCurve.h"
# include "audiocurves/ConstantAudioCurve.h"
# include "audiocurves/CompoundAudioCurve.h"
2009-09-17 13:01:21 +00:00
# include "dsp/Resampler.h"
2007-11-06 21:41:16 +00:00
# include "StretchCalculator.h"
# include "StretcherChannelData.h"
2009-09-17 13:01:21 +00:00
# include "base/Profiler.h"
# ifndef _WIN32
# include <alloca.h>
# endif
2007-11-06 21:41:16 +00:00
# include <cassert>
# include <cmath>
# include <set>
# include <map>
2009-09-17 13:01:21 +00:00
using namespace RubberBand ;
2007-11-06 21:41:16 +00:00
using std : : cerr ;
using std : : endl ;
using std : : vector ;
using std : : map ;
using std : : set ;
using std : : max ;
using std : : min ;
namespace RubberBand {
2007-11-20 20:17:13 +00:00
const size_t
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : m_defaultIncrement = 256 ;
2007-11-20 20:17:13 +00:00
const size_t
2010-05-16 10:44:38 +01:00
RubberBandStretcher : : Impl : : m_defaultFftSize = 2048 ;
2007-11-20 20:17:13 +00:00
int
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : m_defaultDebugLevel = 0 ;
2007-11-20 20:17:13 +00:00
2009-09-17 13:01:21 +00:00
static bool _initialised = false ;
2008-05-22 16:54:27 +00:00
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : Impl ( size_t sampleRate ,
size_t channels ,
Options options ,
double initialTimeRatio ,
double initialPitchScale ) :
2008-06-09 20:46:37 +00:00
m_sampleRate ( sampleRate ) ,
2007-11-06 21:41:16 +00:00
m_channels ( channels ) ,
m_timeRatio ( initialTimeRatio ) ,
m_pitchScale ( initialPitchScale ) ,
2010-05-16 10:44:38 +01:00
m_fftSize ( m_defaultFftSize ) ,
m_aWindowSize ( m_defaultFftSize ) ,
m_sWindowSize ( m_defaultFftSize ) ,
2007-11-20 20:17:13 +00:00
m_increment ( m_defaultIncrement ) ,
2010-05-16 10:44:38 +01:00
m_outbufSize ( m_defaultFftSize * 2 ) ,
m_maxProcessSize ( m_defaultFftSize ) ,
2007-11-06 21:41:16 +00:00
m_expectedInputDuration ( 0 ) ,
2012-09-09 16:57:42 +01:00
# ifndef NO_THREADING
2007-11-06 21:41:16 +00:00
m_threaded ( false ) ,
2012-09-09 16:57:42 +01:00
# endif
2007-11-06 21:41:16 +00:00
m_realtime ( false ) ,
m_options ( options ) ,
2007-11-20 20:17:13 +00:00
m_debugLevel ( m_defaultDebugLevel ) ,
2007-11-06 21:41:16 +00:00
m_mode ( JustCreated ) ,
2010-05-16 10:44:38 +01:00
m_awindow ( 0 ) ,
2010-05-29 22:07:54 +01:00
m_afilter ( 0 ) ,
2010-05-16 10:44:38 +01:00
m_swindow ( 0 ) ,
2007-11-06 21:41:16 +00:00
m_studyFFT ( 0 ) ,
2012-09-09 16:57:42 +01:00
# ifndef NO_THREADING
2007-11-26 21:51:05 +00:00
m_spaceAvailable ( " space " ) ,
2012-09-09 16:57:42 +01:00
# endif
2007-11-06 21:41:16 +00:00
m_inputDuration ( 0 ) ,
2010-03-24 09:44:51 +00:00
m_detectorType ( CompoundAudioCurve : : CompoundDetector ) ,
2008-07-02 21:20:22 +00:00
m_silentHistory ( 0 ) ,
2007-11-06 21:41:16 +00:00
m_lastProcessOutputIncrements ( 16 ) ,
2007-11-19 20:13:39 +00:00
m_lastProcessPhaseResetDf ( 16 ) ,
2010-03-24 09:44:51 +00:00
m_emergencyScavenger ( 10 , 4 ) ,
2007-11-19 20:13:39 +00:00
m_phaseResetAudioCurve ( 0 ) ,
2007-11-06 21:41:16 +00:00
m_stretchAudioCurve ( 0 ) ,
2008-07-02 22:20:56 +00:00
m_silentAudioCurve ( 0 ) ,
2007-11-06 21:41:16 +00:00
m_stretchCalculator ( 0 ) ,
m_freq0 ( 600 ) ,
m_freq1 ( 1200 ) ,
m_freq2 ( 12000 ) ,
2010-05-16 10:44:38 +01:00
m_baseFftSize ( m_defaultFftSize )
2007-11-06 21:41:16 +00:00
{
2009-09-17 13:01:21 +00:00
if ( ! _initialised ) {
system_specific_initialise ( ) ;
_initialised = true ;
}
2008-05-22 16:54:27 +00:00
2007-11-20 20:17:13 +00:00
if ( m_debugLevel > 0 ) {
2008-07-01 10:25:17 +00:00
cerr < < " RubberBandStretcher::Impl::Impl: rate = " < < m_sampleRate < < " , options = " < < options < < endl ;
2007-11-20 20:17:13 +00:00
}
// Window size will vary according to the audio sample rate, but
// we don't let it drop below the 48k default
2008-06-09 20:46:37 +00:00
m_rateMultiple = float ( m_sampleRate ) / 48000.f ;
2010-03-24 09:44:51 +00:00
// if (m_rateMultiple < 1.f) m_rateMultiple = 1.f;
2010-05-16 10:44:38 +01:00
m_baseFftSize = roundUp ( int ( m_defaultFftSize * m_rateMultiple ) ) ;
2007-11-06 21:41:16 +00:00
2008-07-01 10:25:17 +00:00
if ( ( options & OptionWindowShort ) | | ( options & OptionWindowLong ) ) {
if ( ( options & OptionWindowShort ) & & ( options & OptionWindowLong ) ) {
cerr < < " RubberBandStretcher::Impl::Impl: Cannot specify OptionWindowLong and OptionWindowShort together; falling back to OptionWindowStandard " < < endl ;
} else if ( options & OptionWindowShort ) {
2010-05-16 10:44:38 +01:00
m_baseFftSize = m_baseFftSize / 2 ;
2007-11-20 20:17:13 +00:00
if ( m_debugLevel > 0 ) {
2010-05-16 10:44:38 +01:00
cerr < < " setting baseFftSize to " < < m_baseFftSize < < endl ;
2007-11-20 20:17:13 +00:00
}
2008-07-01 10:25:17 +00:00
} else if ( options & OptionWindowLong ) {
2010-05-16 10:44:38 +01:00
m_baseFftSize = m_baseFftSize * 2 ;
2007-11-20 20:17:13 +00:00
if ( m_debugLevel > 0 ) {
2010-05-16 10:44:38 +01:00
cerr < < " setting baseFftSize to " < < m_baseFftSize < < endl ;
2007-11-20 20:17:13 +00:00
}
2007-11-06 21:41:16 +00:00
}
2010-05-16 10:44:38 +01:00
m_fftSize = m_baseFftSize ;
m_aWindowSize = m_baseFftSize ;
m_sWindowSize = m_baseFftSize ;
m_outbufSize = m_sWindowSize * 2 ;
m_maxProcessSize = m_aWindowSize ;
2007-11-06 21:41:16 +00:00
}
2008-07-01 10:25:17 +00:00
if ( m_options & OptionProcessRealTime ) {
2007-11-06 21:41:16 +00:00
m_realtime = true ;
2008-07-01 10:25:17 +00:00
if ( ! ( m_options & OptionStretchPrecise ) ) {
m_options | = OptionStretchPrecise ;
2007-11-06 21:41:16 +00:00
}
}
2012-09-09 16:57:42 +01:00
# ifndef NO_THREADING
2007-11-06 21:41:16 +00:00
if ( m_channels > 1 ) {
2007-11-26 11:50:29 +00:00
m_threaded = true ;
if ( m_realtime ) {
m_threaded = false ;
2008-07-01 10:25:17 +00:00
} else if ( m_options & OptionThreadingNever ) {
2007-11-26 11:50:29 +00:00
m_threaded = false ;
2008-07-01 10:25:17 +00:00
} else if ( ! ( m_options & OptionThreadingAlways ) & &
2007-11-26 11:50:29 +00:00
! system_is_multiprocessor ( ) ) {
m_threaded = false ;
}
2007-11-06 21:41:16 +00:00
2007-11-26 11:50:29 +00:00
if ( m_threaded & & m_debugLevel > 0 ) {
cerr < < " Going multithreaded... " < < endl ;
2007-11-06 21:41:16 +00:00
}
}
2012-09-09 16:57:42 +01:00
# endif
2007-11-06 21:41:16 +00:00
configure ( ) ;
}
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : ~ Impl ( )
2007-11-06 21:41:16 +00:00
{
2012-09-09 16:57:42 +01:00
# ifndef NO_THREADING
2007-11-06 21:41:16 +00:00
if ( m_threaded ) {
MutexLocker locker ( & m_threadSetMutex ) ;
for ( set < ProcessThread * > : : iterator i = m_threadSet . begin ( ) ;
i ! = m_threadSet . end ( ) ; + + i ) {
if ( m_debugLevel > 0 ) {
cerr < < " RubberBandStretcher::~RubberBandStretcher: joining (channel " < < * i < < " ) " < < endl ;
}
2007-12-10 15:47:06 +00:00
( * i ) - > abandon ( ) ;
2007-11-06 21:41:16 +00:00
( * i ) - > wait ( ) ;
delete * i ;
}
}
2012-09-09 16:57:42 +01:00
# endif
2007-11-06 21:41:16 +00:00
for ( size_t c = 0 ; c < m_channels ; + + c ) {
delete m_channelData [ c ] ;
}
2007-11-19 20:13:39 +00:00
delete m_phaseResetAudioCurve ;
2007-11-06 21:41:16 +00:00
delete m_stretchAudioCurve ;
2008-07-02 21:20:22 +00:00
delete m_silentAudioCurve ;
2007-11-06 21:41:16 +00:00
delete m_stretchCalculator ;
delete m_studyFFT ;
for ( map < size_t , Window < float > * > : : iterator i = m_windows . begin ( ) ;
i ! = m_windows . end ( ) ; + + i ) {
delete i - > second ;
}
2010-05-25 21:49:05 +01:00
for ( map < size_t , SincWindow < float > * > : : iterator i = m_sincs . begin ( ) ;
i ! = m_sincs . end ( ) ; + + i ) {
delete i - > second ;
}
2007-11-06 21:41:16 +00:00
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : reset ( )
2007-11-06 21:41:16 +00:00
{
2012-09-09 16:57:42 +01:00
# ifndef NO_THREADING
2007-12-10 15:47:06 +00:00
if ( m_threaded ) {
m_threadSetMutex . lock ( ) ;
for ( set < ProcessThread * > : : iterator i = m_threadSet . begin ( ) ;
i ! = m_threadSet . end ( ) ; + + i ) {
if ( m_debugLevel > 0 ) {
cerr < < " RubberBandStretcher::~RubberBandStretcher: joining (channel " < < * i < < " ) " < < endl ;
}
( * i ) - > abandon ( ) ;
( * i ) - > wait ( ) ;
delete * i ;
}
m_threadSet . clear ( ) ;
}
2012-09-09 16:57:42 +01:00
# endif
2007-11-06 21:41:16 +00:00
2010-03-24 09:44:51 +00:00
m_emergencyScavenger . scavenge ( ) ;
if ( m_stretchCalculator ) {
m_stretchCalculator - > setKeyFrameMap ( std : : map < size_t , size_t > ( ) ) ;
}
2007-11-06 21:41:16 +00:00
for ( size_t c = 0 ; c < m_channels ; + + c ) {
2007-12-10 15:47:06 +00:00
m_channelData [ c ] - > reset ( ) ;
2007-11-06 21:41:16 +00:00
}
2007-12-10 15:47:06 +00:00
2007-11-06 21:41:16 +00:00
m_mode = JustCreated ;
2007-11-19 20:13:39 +00:00
if ( m_phaseResetAudioCurve ) m_phaseResetAudioCurve - > reset ( ) ;
2007-11-06 21:41:16 +00:00
if ( m_stretchAudioCurve ) m_stretchAudioCurve - > reset ( ) ;
2008-07-02 21:20:22 +00:00
if ( m_silentAudioCurve ) m_silentAudioCurve - > reset ( ) ;
2007-11-06 21:41:16 +00:00
m_inputDuration = 0 ;
2008-07-02 21:20:22 +00:00
m_silentHistory = 0 ;
2007-11-06 21:41:16 +00:00
2012-09-09 16:57:42 +01:00
# ifndef NO_THREADING
2007-11-06 21:41:16 +00:00
if ( m_threaded ) m_threadSetMutex . unlock ( ) ;
2012-09-09 16:57:42 +01:00
# endif
2007-12-10 15:47:06 +00:00
reconfigure ( ) ;
2007-11-06 21:41:16 +00:00
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : setTimeRatio ( double ratio )
2007-11-06 21:41:16 +00:00
{
if ( ! m_realtime ) {
if ( m_mode = = Studying | | m_mode = = Processing ) {
2008-07-01 10:25:17 +00:00
cerr < < " RubberBandStretcher::Impl::setTimeRatio: Cannot set ratio while studying or processing in non-RT mode " < < endl ;
2007-11-06 21:41:16 +00:00
return ;
}
}
if ( ratio = = m_timeRatio ) return ;
m_timeRatio = ratio ;
reconfigure ( ) ;
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : setPitchScale ( double fs )
2007-11-06 21:41:16 +00:00
{
if ( ! m_realtime ) {
if ( m_mode = = Studying | | m_mode = = Processing ) {
2008-07-01 10:25:17 +00:00
cerr < < " RubberBandStretcher::Impl::setPitchScale: Cannot set ratio while studying or processing in non-RT mode " < < endl ;
2007-11-06 21:41:16 +00:00
return ;
}
}
if ( fs = = m_pitchScale ) return ;
2008-07-04 19:31:23 +00:00
2008-07-04 14:19:32 +00:00
bool was1 = ( m_pitchScale = = 1.f ) ;
bool rbs = resampleBeforeStretching ( ) ;
2007-11-06 21:41:16 +00:00
m_pitchScale = fs ;
reconfigure ( ) ;
2008-07-04 14:19:32 +00:00
if ( ! ( m_options & OptionPitchHighConsistency ) & &
( was1 | | resampleBeforeStretching ( ) ! = rbs ) & &
m_pitchScale ! = 1.f ) {
2008-07-04 19:31:23 +00:00
2008-07-04 14:19:32 +00:00
// resampling mode has changed
2008-07-07 18:54:18 +00:00
for ( int c = 0 ; c < int ( m_channels ) ; + + c ) {
2008-07-04 14:19:32 +00:00
if ( m_channelData [ c ] - > resampler ) {
m_channelData [ c ] - > resampler - > reset ( ) ;
}
}
}
2007-11-06 21:41:16 +00:00
}
double
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : getTimeRatio ( ) const
2007-11-06 21:41:16 +00:00
{
return m_timeRatio ;
}
double
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : getPitchScale ( ) const
2007-11-06 21:41:16 +00:00
{
return m_pitchScale ;
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : setExpectedInputDuration ( size_t samples )
2007-11-06 21:41:16 +00:00
{
if ( samples = = m_expectedInputDuration ) return ;
m_expectedInputDuration = samples ;
reconfigure ( ) ;
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : setMaxProcessSize ( size_t samples )
2007-11-06 21:41:16 +00:00
{
2007-11-18 21:38:18 +00:00
if ( samples < = m_maxProcessSize ) return ;
m_maxProcessSize = samples ;
2007-11-06 21:41:16 +00:00
reconfigure ( ) ;
}
2010-03-24 09:44:51 +00:00
void
RubberBandStretcher : : Impl : : setKeyFrameMap ( const std : : map < size_t , size_t > &
mapping )
{
if ( m_realtime ) {
cerr < < " RubberBandStretcher::Impl::setKeyFrameMap: Cannot specify key frame map in RT mode " < < endl ;
return ;
}
if ( m_mode = = Processing ) {
cerr < < " RubberBandStretcher::Impl::setKeyFrameMap: Cannot specify key frame map after process() has begun " < < endl ;
return ;
}
if ( m_stretchCalculator ) {
m_stretchCalculator - > setKeyFrameMap ( mapping ) ;
}
}
2007-11-06 21:41:16 +00:00
float
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : getFrequencyCutoff ( int n ) const
2007-11-06 21:41:16 +00:00
{
switch ( n ) {
case 0 : return m_freq0 ;
case 1 : return m_freq1 ;
case 2 : return m_freq2 ;
}
return 0.f ;
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : setFrequencyCutoff ( int n , float f )
2007-11-06 21:41:16 +00:00
{
switch ( n ) {
case 0 : m_freq0 = f ; break ;
case 1 : m_freq1 = f ; break ;
case 2 : m_freq2 = f ; break ;
}
}
double
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : getEffectiveRatio ( ) const
2007-11-06 21:41:16 +00:00
{
// Returns the ratio that the internal time stretcher needs to
// achieve, not the resulting duration ratio of the output (which
// is simply m_timeRatio).
// A frequency shift is achieved using an additional time shift,
// followed by resampling back to the original time shift to
// change the pitch. Note that the resulting frequency change is
// fixed, as it is effected by the resampler -- in contrast to
// time shifting, which is variable aiming to place the majority
// of the stretch or squash in low-interest regions of audio.
return m_timeRatio * m_pitchScale ;
}
size_t
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : roundUp ( size_t value )
2007-11-06 21:41:16 +00:00
{
if ( ! ( value & ( value - 1 ) ) ) return value ;
int bits = 0 ;
while ( value ) { + + bits ; value > > = 1 ; }
value = 1 < < bits ;
return value ;
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : calculateSizes ( )
2007-11-06 21:41:16 +00:00
{
2007-11-20 20:17:13 +00:00
size_t inputIncrement = m_defaultIncrement ;
2010-05-16 10:44:38 +01:00
size_t windowSize = m_baseFftSize ;
2007-11-06 21:41:16 +00:00
size_t outputIncrement ;
2008-10-13 19:13:21 +00:00
if ( m_pitchScale < = 0.0 ) {
// This special case is likelier than one might hope, because
// of naive initialisations in programs that set it from a
// variable
std : : cerr < < " RubberBandStretcher: WARNING: Pitch scale must be greater than zero! \n Resetting it from " < < m_pitchScale < < " to the default of 1.0: no pitch change will occur " < < std : : endl ;
m_pitchScale = 1.0 ;
}
if ( m_timeRatio < = 0.0 ) {
// Likewise
std : : cerr < < " RubberBandStretcher: WARNING: Time ratio must be greater than zero! \n Resetting it from " < < m_timeRatio < < " to the default of 1.0: no time stretch will occur " < < std : : endl ;
m_timeRatio = 1.0 ;
}
2007-11-06 21:41:16 +00:00
double r = getEffectiveRatio ( ) ;
if ( m_realtime ) {
if ( r < 1 ) {
2008-05-22 16:54:27 +00:00
bool rsb = ( m_pitchScale < 1.0 & & ! resampleBeforeStretching ( ) ) ;
float windowIncrRatio = 4.5 ;
if ( r = = 1.0 ) windowIncrRatio = 4 ;
else if ( rsb ) windowIncrRatio = 4.5 ;
else windowIncrRatio = 6 ;
inputIncrement = int ( windowSize / windowIncrRatio ) ;
2007-11-06 21:41:16 +00:00
outputIncrement = int ( floor ( inputIncrement * r ) ) ;
2008-05-22 16:54:27 +00:00
// Very long stretch or very low pitch shift
if ( outputIncrement < m_defaultIncrement / 4 ) {
if ( outputIncrement < 1 ) outputIncrement = 1 ;
while ( outputIncrement < m_defaultIncrement / 4 & &
2010-05-16 10:44:38 +01:00
windowSize < m_baseFftSize * 4 ) {
2008-05-22 16:54:27 +00:00
outputIncrement * = 2 ;
inputIncrement = lrint ( ceil ( outputIncrement / r ) ) ;
windowSize = roundUp ( lrint ( ceil ( inputIncrement * windowIncrRatio ) ) ) ;
}
2007-11-06 21:41:16 +00:00
}
2008-05-22 16:54:27 +00:00
2007-11-06 21:41:16 +00:00
} else {
2008-05-22 16:54:27 +00:00
bool rsb = ( m_pitchScale > 1.0 & & resampleBeforeStretching ( ) ) ;
float windowIncrRatio = 4.5 ;
if ( r = = 1.0 ) windowIncrRatio = 4 ;
else if ( rsb ) windowIncrRatio = 4.5 ;
2010-03-24 09:44:51 +00:00
else windowIncrRatio = 8 ;
2008-05-22 16:54:27 +00:00
outputIncrement = int ( windowSize / windowIncrRatio ) ;
inputIncrement = int ( outputIncrement / r ) ;
while ( outputIncrement > 1024 * m_rateMultiple & &
inputIncrement > 1 ) {
outputIncrement / = 2 ;
inputIncrement = int ( outputIncrement / r ) ;
}
2008-07-02 21:20:22 +00:00
size_t minwin = roundUp ( lrint ( outputIncrement * windowIncrRatio ) ) ;
if ( windowSize < minwin ) windowSize = minwin ;
2008-05-22 16:54:27 +00:00
if ( rsb ) {
// cerr << "adjusting window size from " << windowSize;
size_t newWindowSize = roundUp ( lrint ( windowSize / m_pitchScale ) ) ;
if ( newWindowSize < 512 ) newWindowSize = 512 ;
size_t div = windowSize / newWindowSize ;
if ( inputIncrement > div & & outputIncrement > div ) {
inputIncrement / = div ;
outputIncrement / = div ;
windowSize / = div ;
}
// cerr << " to " << windowSize << " (inputIncrement = " << inputIncrement << ", outputIncrement = " << outputIncrement << ")" << endl;
2007-11-06 21:41:16 +00:00
}
}
} else {
if ( r < 1 ) {
2007-11-18 21:38:18 +00:00
inputIncrement = windowSize / 4 ;
2007-11-06 21:41:16 +00:00
while ( inputIncrement > = 512 ) inputIncrement / = 2 ;
outputIncrement = int ( floor ( inputIncrement * r ) ) ;
if ( outputIncrement < 1 ) {
outputIncrement = 1 ;
inputIncrement = roundUp ( lrint ( ceil ( outputIncrement / r ) ) ) ;
2007-11-18 21:38:18 +00:00
windowSize = inputIncrement * 4 ;
2007-11-06 21:41:16 +00:00
}
} else {
2007-11-18 21:38:18 +00:00
outputIncrement = windowSize / 6 ;
2007-11-06 21:41:16 +00:00
inputIncrement = int ( outputIncrement / r ) ;
while ( outputIncrement > 1024 & & inputIncrement > 1 ) {
outputIncrement / = 2 ;
inputIncrement = int ( outputIncrement / r ) ;
}
2007-11-18 21:38:18 +00:00
windowSize = std : : max ( windowSize , roundUp ( outputIncrement * 6 ) ) ;
if ( r > 5 ) while ( windowSize < 8192 ) windowSize * = 2 ;
2007-11-06 21:41:16 +00:00
}
2008-05-22 16:54:27 +00:00
}
2007-11-06 21:41:16 +00:00
if ( m_expectedInputDuration > 0 ) {
while ( inputIncrement * 4 > m_expectedInputDuration & &
inputIncrement > 1 ) {
inputIncrement / = 2 ;
}
}
2010-05-25 21:49:05 +01:00
// m_fftSize can be almost anything, but it can't be greater than
2010-05-16 10:44:38 +01:00
// 4 * m_baseFftSize unless ratio is less than 1/1024.
2007-11-06 21:41:16 +00:00
2010-05-16 10:44:38 +01:00
m_fftSize = windowSize ;
2010-05-29 22:07:54 +01:00
if ( m_options & OptionSmoothingOn ) {
m_aWindowSize = windowSize * 2 ;
m_sWindowSize = windowSize * 2 ;
} else {
m_aWindowSize = windowSize ;
m_sWindowSize = windowSize ;
}
2007-11-06 21:41:16 +00:00
m_increment = inputIncrement ;
// When squashing, the greatest theoretically possible output
// increment is the input increment. When stretching adaptively
// the sky's the limit in principle, but we expect
// StretchCalculator to restrict itself to using no more than
// twice the basic output increment (i.e. input increment times
2007-11-18 21:38:18 +00:00
// ratio) for any chunk.
2007-11-06 21:41:16 +00:00
if ( m_debugLevel > 0 ) {
cerr < < " configure: effective ratio = " < < getEffectiveRatio ( ) < < endl ;
2010-05-16 10:44:38 +01:00
cerr < < " configure: analysis window size = " < < m_aWindowSize < < " , synthesis window size = " < < m_sWindowSize < < " , fft size = " < < m_fftSize < < " , increment = " < < m_increment < < " (approx output increment = " < < int ( lrint ( m_increment * getEffectiveRatio ( ) ) ) < < " ) " < < endl ;
2007-11-06 21:41:16 +00:00
}
2010-05-16 10:44:38 +01:00
if ( std : : max ( m_aWindowSize , m_sWindowSize ) > m_maxProcessSize ) {
m_maxProcessSize = std : : max ( m_aWindowSize , m_sWindowSize ) ;
2007-11-06 21:41:16 +00:00
}
2007-11-21 21:48:16 +00:00
m_outbufSize =
size_t
( ceil ( max
( m_maxProcessSize / m_pitchScale ,
2010-05-25 21:49:05 +01:00
m_maxProcessSize * 2 * ( m_timeRatio > 1.f ? m_timeRatio : 1.f ) ) ) ) ;
2007-11-06 21:41:16 +00:00
if ( m_realtime ) {
// This headroom is so as to try to avoid reallocation when
// the pitch scale changes
m_outbufSize = m_outbufSize * 16 ;
} else {
2012-09-09 16:57:42 +01:00
# ifndef NO_THREADING
2007-11-06 21:41:16 +00:00
if ( m_threaded ) {
// This headroom is to permit the processing threads to
// run ahead of the buffer output drainage; the exact
// amount of headroom is a question of tuning rather than
// results
m_outbufSize = m_outbufSize * 16 ;
}
2012-09-09 16:57:42 +01:00
# endif
2007-11-06 21:41:16 +00:00
}
2007-11-21 21:48:16 +00:00
if ( m_debugLevel > 0 ) {
cerr < < " configure: outbuf size = " < < m_outbufSize < < endl ;
}
2007-11-06 21:41:16 +00:00
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : configure ( )
2007-11-06 21:41:16 +00:00
{
2007-12-10 16:29:52 +00:00
// std::cerr << "configure[" << this << "]: realtime = " << m_realtime << ", pitch scale = "
// << m_pitchScale << ", channels = " << m_channels << std::endl;
2007-12-10 15:47:06 +00:00
2010-05-16 10:44:38 +01:00
size_t prevFftSize = m_fftSize ;
size_t prevAWindowSize = m_aWindowSize ;
size_t prevSWindowSize = m_sWindowSize ;
2007-11-06 21:41:16 +00:00
size_t prevOutbufSize = m_outbufSize ;
if ( m_windows . empty ( ) ) {
2010-05-16 10:44:38 +01:00
prevFftSize = 0 ;
prevAWindowSize = 0 ;
prevSWindowSize = 0 ;
2007-11-06 21:41:16 +00:00
prevOutbufSize = 0 ;
}
calculateSizes ( ) ;
2010-05-16 10:44:38 +01:00
bool fftSizeChanged = ( prevFftSize ! = m_fftSize ) ;
bool windowSizeChanged = ( ( prevAWindowSize ! = m_aWindowSize ) | |
( prevSWindowSize ! = m_sWindowSize ) ) ;
2007-11-06 21:41:16 +00:00
bool outbufSizeChanged = ( prevOutbufSize ! = m_outbufSize ) ;
// This function may be called at any time in non-RT mode, after a
// parameter has changed. It shouldn't be legal to call it after
// processing has already begun.
// This function is only called once (on construction) in RT
// mode. After that reconfigure() does the work in a hopefully
// RT-safe way.
2007-11-18 21:38:18 +00:00
set < size_t > windowSizes ;
2007-11-06 21:41:16 +00:00
if ( m_realtime ) {
2010-05-16 10:44:38 +01:00
windowSizes . insert ( m_baseFftSize ) ;
windowSizes . insert ( m_baseFftSize / 2 ) ;
windowSizes . insert ( m_baseFftSize * 2 ) ;
// windowSizes.insert(m_baseFftSize * 4);
2007-11-06 21:41:16 +00:00
}
2010-05-16 10:44:38 +01:00
windowSizes . insert ( m_fftSize ) ;
windowSizes . insert ( m_aWindowSize ) ;
windowSizes . insert ( m_sWindowSize ) ;
2007-11-06 21:41:16 +00:00
2007-11-18 21:38:18 +00:00
if ( windowSizeChanged ) {
2007-11-06 21:41:16 +00:00
2007-11-18 21:38:18 +00:00
for ( set < size_t > : : const_iterator i = windowSizes . begin ( ) ;
i ! = windowSizes . end ( ) ; + + i ) {
2007-11-06 21:41:16 +00:00
if ( m_windows . find ( * i ) = = m_windows . end ( ) ) {
m_windows [ * i ] = new Window < float > ( HanningWindow , * i ) ;
}
2010-05-25 21:49:05 +01:00
if ( m_sincs . find ( * i ) = = m_sincs . end ( ) ) {
m_sincs [ * i ] = new SincWindow < float > ( * i , * i ) ;
}
2007-11-06 21:41:16 +00:00
}
2010-05-16 10:44:38 +01:00
m_awindow = m_windows [ m_aWindowSize ] ;
2010-05-29 22:07:54 +01:00
m_afilter = m_sincs [ m_aWindowSize ] ;
2010-05-16 10:44:38 +01:00
m_swindow = m_windows [ m_sWindowSize ] ;
2007-11-06 21:41:16 +00:00
if ( m_debugLevel > 0 ) {
2010-05-16 10:44:38 +01:00
cerr < < " Window area: " < < m_awindow - > getArea ( ) < < " ; synthesis window area: " < < m_swindow - > getArea ( ) < < endl ;
2007-11-06 21:41:16 +00:00
}
}
2007-11-18 21:38:18 +00:00
if ( windowSizeChanged | | outbufSizeChanged ) {
2007-11-06 21:41:16 +00:00
for ( size_t c = 0 ; c < m_channelData . size ( ) ; + + c ) {
delete m_channelData [ c ] ;
}
m_channelData . clear ( ) ;
for ( size_t c = 0 ; c < m_channels ; + + c ) {
m_channelData . push_back
2010-05-16 10:44:38 +01:00
( new ChannelData ( windowSizes ,
std : : max ( m_aWindowSize , m_sWindowSize ) ,
m_fftSize ,
m_outbufSize ) ) ;
2007-11-06 21:41:16 +00:00
}
}
2010-05-16 10:44:38 +01:00
if ( ! m_realtime & & fftSizeChanged ) {
2007-11-06 21:41:16 +00:00
delete m_studyFFT ;
2010-05-16 10:44:38 +01:00
m_studyFFT = new FFT ( m_fftSize , m_debugLevel ) ;
2007-11-06 21:41:16 +00:00
m_studyFFT - > initFloat ( ) ;
}
2008-07-01 20:49:24 +00:00
if ( m_pitchScale ! = 1.0 | |
( m_options & OptionPitchHighConsistency ) | |
m_realtime ) {
2007-11-06 21:41:16 +00:00
for ( size_t c = 0 ; c < m_channels ; + + c ) {
if ( m_channelData [ c ] - > resampler ) continue ;
m_channelData [ c ] - > resampler =
2008-05-22 16:54:27 +00:00
new Resampler ( Resampler : : FastestTolerable , 1 , 4096 * 16 ,
m_debugLevel ) ;
2007-11-06 21:41:16 +00:00
2007-11-21 21:48:16 +00:00
// rbs is the amount of buffer space we think we'll need
// for resampling; but allocate a sensible amount in case
// the pitch scale changes during use
size_t rbs =
2007-11-06 21:41:16 +00:00
lrintf ( ceil ( ( m_increment * m_timeRatio * 2 ) / m_pitchScale ) ) ;
2007-11-21 21:48:16 +00:00
if ( rbs < m_increment * 16 ) rbs = m_increment * 16 ;
2008-05-22 16:54:27 +00:00
m_channelData [ c ] - > setResampleBufSize ( rbs ) ;
2007-11-06 21:41:16 +00:00
}
}
2008-07-02 21:20:22 +00:00
// stretchAudioCurve is unused in RT mode; phaseResetAudioCurve,
// silentAudioCurve and stretchCalculator however are used in all
// modes
2007-11-19 20:13:39 +00:00
delete m_phaseResetAudioCurve ;
2010-03-24 09:44:51 +00:00
m_phaseResetAudioCurve = new CompoundAudioCurve
2010-05-16 10:44:38 +01:00
( CompoundAudioCurve : : Parameters ( m_sampleRate , m_fftSize ) ) ;
2010-03-24 09:44:51 +00:00
m_phaseResetAudioCurve - > setType ( m_detectorType ) ;
2007-11-06 21:41:16 +00:00
2008-07-02 21:20:22 +00:00
delete m_silentAudioCurve ;
m_silentAudioCurve = new SilentAudioCurve
2010-05-16 10:44:38 +01:00
( SilentAudioCurve : : Parameters ( m_sampleRate , m_fftSize ) ) ;
2007-11-06 21:41:16 +00:00
if ( ! m_realtime ) {
delete m_stretchAudioCurve ;
2008-07-01 10:25:17 +00:00
if ( ! ( m_options & OptionStretchPrecise ) ) {
2007-12-10 15:47:06 +00:00
m_stretchAudioCurve = new SpectralDifferenceAudioCurve
2010-05-16 10:44:38 +01:00
( SpectralDifferenceAudioCurve : : Parameters ( m_sampleRate , m_fftSize ) ) ;
2007-11-06 21:41:16 +00:00
} else {
m_stretchAudioCurve = new ConstantAudioCurve
2010-05-16 10:44:38 +01:00
( ConstantAudioCurve : : Parameters ( m_sampleRate , m_fftSize ) ) ;
2007-11-06 21:41:16 +00:00
}
}
delete m_stretchCalculator ;
m_stretchCalculator = new StretchCalculator
2008-06-09 20:46:37 +00:00
( m_sampleRate , m_increment ,
2008-07-01 10:25:17 +00:00
! ( m_options & OptionTransientsSmooth ) ) ;
2007-11-06 21:41:16 +00:00
m_stretchCalculator - > setDebugLevel ( m_debugLevel ) ;
m_inputDuration = 0 ;
2007-11-18 21:38:18 +00:00
// Prepare the inbufs with half a chunk of emptiness. The centre
// point of the first processing chunk for the onset detector
2007-11-06 21:41:16 +00:00
// should be the first sample of the audio, and we continue until
2007-11-18 21:38:18 +00:00
// we can no longer centre a chunk within the input audio. The
// number of onset detector chunks will be the number of audio
2007-11-06 21:41:16 +00:00
// samples input, divided by the input increment, plus one.
// In real-time mode, we don't do this prefill -- it's better to
// start with a swoosh than introduce more latency, and we don't
// want gaps when the ratio changes.
if ( ! m_realtime ) {
2011-11-25 11:11:59 +00:00
if ( m_debugLevel > 1 ) {
cerr < < " Not real time mode: prefilling " < < endl ;
}
2007-11-06 21:41:16 +00:00
for ( size_t c = 0 ; c < m_channels ; + + c ) {
m_channelData [ c ] - > reset ( ) ;
2010-05-16 10:44:38 +01:00
m_channelData [ c ] - > inbuf - > zero ( m_aWindowSize / 2 ) ;
2007-11-06 21:41:16 +00:00
}
}
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : reconfigure ( )
2007-11-06 21:41:16 +00:00
{
if ( ! m_realtime ) {
if ( m_mode = = Studying ) {
// stop and calculate the stretch curve so far, then reset
// the df vectors
calculateStretch ( ) ;
2007-11-19 20:13:39 +00:00
m_phaseResetDf . clear ( ) ;
2007-11-06 21:41:16 +00:00
m_stretchDf . clear ( ) ;
2008-07-02 21:20:22 +00:00
m_silence . clear ( ) ;
2007-11-06 21:41:16 +00:00
m_inputDuration = 0 ;
}
configure ( ) ;
}
2010-05-16 10:44:38 +01:00
size_t prevFftSize = m_fftSize ;
size_t prevAWindowSize = m_aWindowSize ;
size_t prevSWindowSize = m_sWindowSize ;
2007-11-06 21:41:16 +00:00
size_t prevOutbufSize = m_outbufSize ;
calculateSizes ( ) ;
// There are various allocations in this function, but they should
// never happen in normal use -- they just recover from the case
// where not all of the things we need were correctly created when
// we first configured (for whatever reason). This is intended to
// be "effectively" realtime safe. The same goes for
2010-05-16 10:44:38 +01:00
// ChannelData::setOutbufSize and setSizes.
2007-11-06 21:41:16 +00:00
2010-05-16 10:44:38 +01:00
if ( m_aWindowSize ! = prevAWindowSize | |
m_sWindowSize ! = prevSWindowSize ) {
2007-11-06 21:41:16 +00:00
2010-05-16 10:44:38 +01:00
if ( m_windows . find ( m_aWindowSize ) = = m_windows . end ( ) ) {
std : : cerr < < " WARNING: reconfigure(): window allocation (size " < < m_aWindowSize < < " ) required in RT mode " < < std : : endl ;
m_windows [ m_aWindowSize ] = new Window < float >
( HanningWindow , m_aWindowSize ) ;
2010-05-25 21:49:05 +01:00
m_sincs [ m_aWindowSize ] = new SincWindow < float >
( m_aWindowSize , m_aWindowSize ) ;
2007-11-06 21:41:16 +00:00
}
2010-05-16 10:44:38 +01:00
if ( m_windows . find ( m_sWindowSize ) = = m_windows . end ( ) ) {
std : : cerr < < " WARNING: reconfigure(): window allocation (size " < < m_sWindowSize < < " ) required in RT mode " < < std : : endl ;
m_windows [ m_sWindowSize ] = new Window < float >
( HanningWindow , m_sWindowSize ) ;
2010-05-25 21:49:05 +01:00
m_sincs [ m_sWindowSize ] = new SincWindow < float >
( m_sWindowSize , m_sWindowSize ) ;
2010-05-16 10:44:38 +01:00
}
m_awindow = m_windows [ m_aWindowSize ] ;
2010-05-29 22:07:54 +01:00
m_afilter = m_sincs [ m_aWindowSize ] ;
2010-05-16 10:44:38 +01:00
m_swindow = m_windows [ m_sWindowSize ] ;
2007-11-06 21:41:16 +00:00
for ( size_t c = 0 ; c < m_channels ; + + c ) {
2010-05-16 10:44:38 +01:00
m_channelData [ c ] - > setSizes ( std : : max ( m_aWindowSize , m_sWindowSize ) ,
m_fftSize ) ;
2007-11-06 21:41:16 +00:00
}
}
if ( m_outbufSize ! = prevOutbufSize ) {
for ( size_t c = 0 ; c < m_channels ; + + c ) {
m_channelData [ c ] - > setOutbufSize ( m_outbufSize ) ;
}
}
if ( m_pitchScale ! = 1.0 ) {
for ( size_t c = 0 ; c < m_channels ; + + c ) {
if ( m_channelData [ c ] - > resampler ) continue ;
std : : cerr < < " WARNING: reconfigure(): resampler construction required in RT mode " < < std : : endl ;
m_channelData [ c ] - > resampler =
2010-05-16 10:44:38 +01:00
new Resampler ( Resampler : : FastestTolerable , 1 , m_sWindowSize ,
2008-05-22 16:54:27 +00:00
m_debugLevel ) ;
2007-11-06 21:41:16 +00:00
2011-03-19 12:41:38 +00:00
size_t rbs =
lrintf ( ceil ( ( m_increment * m_timeRatio * 2 ) / m_pitchScale ) ) ;
if ( rbs < m_increment * 16 ) rbs = m_increment * 16 ;
m_channelData [ c ] - > setResampleBufSize ( rbs ) ;
2007-11-06 21:41:16 +00:00
}
}
2010-05-16 10:44:38 +01:00
if ( m_fftSize ! = prevFftSize ) {
m_phaseResetAudioCurve - > setFftSize ( m_fftSize ) ;
2007-11-06 21:41:16 +00:00
}
}
size_t
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : getLatency ( ) const
2007-11-06 21:41:16 +00:00
{
if ( ! m_realtime ) return 0 ;
2010-05-16 10:44:38 +01:00
return int ( ( m_aWindowSize / 2 ) / m_pitchScale + 1 ) ;
2007-11-06 21:41:16 +00:00
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : setTransientsOption ( Options options )
2007-11-06 21:41:16 +00:00
{
2007-11-21 19:57:49 +00:00
if ( ! m_realtime ) {
2008-07-01 10:25:17 +00:00
cerr < < " RubberBandStretcher::Impl::setTransientsOption: Not permissible in non-realtime mode " < < endl ;
2007-11-21 19:57:49 +00:00
return ;
}
2008-07-01 10:25:17 +00:00
int mask = ( OptionTransientsMixed | OptionTransientsSmooth | OptionTransientsCrisp ) ;
2008-05-22 16:54:27 +00:00
m_options & = ~ mask ;
options & = mask ;
2007-11-20 20:17:13 +00:00
m_options | = options ;
2007-11-21 19:57:49 +00:00
m_stretchCalculator - > setUseHardPeaks
2008-07-01 10:25:17 +00:00
( ! ( m_options & OptionTransientsSmooth ) ) ;
2007-11-06 21:41:16 +00:00
}
2010-03-24 09:44:51 +00:00
void
RubberBandStretcher : : Impl : : setDetectorOption ( Options options )
{
if ( ! m_realtime ) {
cerr < < " RubberBandStretcher::Impl::setDetectorOption: Not permissible in non-realtime mode " < < endl ;
return ;
}
int mask = ( OptionDetectorPercussive | OptionDetectorCompound | OptionDetectorSoft ) ;
m_options & = ~ mask ;
options & = mask ;
m_options | = options ;
CompoundAudioCurve : : Type dt = CompoundAudioCurve : : CompoundDetector ;
if ( m_options & OptionDetectorPercussive ) dt = CompoundAudioCurve : : PercussiveDetector ;
else if ( m_options & OptionDetectorSoft ) dt = CompoundAudioCurve : : SoftDetector ;
if ( dt = = m_detectorType ) return ;
m_detectorType = dt ;
if ( m_phaseResetAudioCurve ) {
m_phaseResetAudioCurve - > setType ( m_detectorType ) ;
}
}
2007-11-06 21:41:16 +00:00
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : setPhaseOption ( Options options )
2007-11-06 21:41:16 +00:00
{
2008-07-08 15:00:22 +00:00
int mask = ( OptionPhaseLaminar | OptionPhaseIndependent ) ;
2008-05-22 16:54:27 +00:00
m_options & = ~ mask ;
options & = mask ;
m_options | = options ;
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : setFormantOption ( Options options )
2008-05-22 16:54:27 +00:00
{
2008-07-01 10:25:17 +00:00
int mask = ( OptionFormantShifted | OptionFormantPreserved ) ;
2008-05-22 16:54:27 +00:00
m_options & = ~ mask ;
options & = mask ;
m_options | = options ;
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : setPitchOption ( Options options )
2008-05-22 16:54:27 +00:00
{
if ( ! m_realtime ) {
2008-07-01 10:25:17 +00:00
cerr < < " RubberBandStretcher::Impl::setPitchOption: Pitch option is not used in non-RT mode " < < endl ;
2008-05-22 16:54:27 +00:00
return ;
}
2008-07-01 10:25:17 +00:00
Options prior = m_options ;
2008-05-22 16:54:27 +00:00
2008-07-01 20:49:24 +00:00
int mask = ( OptionPitchHighQuality |
OptionPitchHighSpeed |
OptionPitchHighConsistency ) ;
2008-05-22 16:54:27 +00:00
m_options & = ~ mask ;
options & = mask ;
2007-11-20 20:17:13 +00:00
m_options | = options ;
2008-05-22 16:54:27 +00:00
if ( prior ! = m_options ) reconfigure ( ) ;
2007-11-06 21:41:16 +00:00
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : study ( const float * const * input , size_t samples , bool final )
2007-11-06 21:41:16 +00:00
{
2008-07-01 10:25:17 +00:00
Profiler profiler ( " RubberBandStretcher::Impl::study " ) ;
2008-05-22 16:54:27 +00:00
2007-11-06 21:41:16 +00:00
if ( m_realtime ) {
if ( m_debugLevel > 1 ) {
2008-07-01 10:25:17 +00:00
cerr < < " RubberBandStretcher::Impl::study: Not meaningful in realtime mode " < < endl ;
2007-11-06 21:41:16 +00:00
}
return ;
}
if ( m_mode = = Processing | | m_mode = = Finished ) {
2008-07-01 10:25:17 +00:00
cerr < < " RubberBandStretcher::Impl::study: Cannot study after processing " < < endl ;
2007-11-06 21:41:16 +00:00
return ;
}
m_mode = Studying ;
size_t consumed = 0 ;
ChannelData & cd = * m_channelData [ 0 ] ;
RingBuffer < float > & inbuf = * cd . inbuf ;
const float * mixdown ;
float * mdalloc = 0 ;
if ( m_channels > 1 | | final ) {
// mix down into a single channel for analysis
mdalloc = new float [ samples ] ;
for ( size_t i = 0 ; i < samples ; + + i ) {
if ( i < samples ) {
mdalloc [ i ] = input [ 0 ] [ i ] ;
} else {
mdalloc [ i ] = 0.f ;
}
}
for ( size_t c = 1 ; c < m_channels ; + + c ) {
for ( size_t i = 0 ; i < samples ; + + i ) {
mdalloc [ i ] + = input [ c ] [ i ] ;
}
}
for ( size_t i = 0 ; i < samples ; + + i ) {
mdalloc [ i ] / = m_channels ;
}
mixdown = mdalloc ;
} else {
mixdown = input [ 0 ] ;
}
while ( consumed < samples ) {
size_t writable = inbuf . getWriteSpace ( ) ;
writable = min ( writable , samples - consumed ) ;
if ( writable = = 0 ) {
// warn
cerr < < " WARNING: writable == 0 (consumed = " < < consumed < < " , samples = " < < samples < < " ) " < < endl ;
} else {
inbuf . write ( mixdown + consumed , writable ) ;
consumed + = writable ;
}
2010-05-16 10:44:38 +01:00
while ( ( inbuf . getReadSpace ( ) > = int ( m_aWindowSize ) ) | |
( final & & ( inbuf . getReadSpace ( ) > = int ( m_aWindowSize / 2 ) ) ) ) {
2007-11-06 21:41:16 +00:00
2010-05-16 10:44:38 +01:00
// We know we have at least m_aWindowSize samples
// available in m_inbuf. We need to peek m_aWindowSize of
// them for processing, and then skip m_increment to
// advance the read pointer.
2007-11-06 21:41:16 +00:00
// cd.accumulator is not otherwise used during studying,
// so we can use it as a temporary buffer here
2011-11-25 11:11:59 +00:00
size_t ready = inbuf . getReadSpace ( ) ;
assert ( final | | ready > = m_aWindowSize ) ;
inbuf . peek ( cd . accumulator , std : : min ( ready , m_aWindowSize ) ) ;
2010-05-16 10:44:38 +01:00
2010-05-25 21:49:05 +01:00
if ( m_aWindowSize = = m_fftSize ) {
// We don't need the fftshift for studying, as we're
// only interested in magnitude.
m_awindow - > cut ( cd . accumulator ) ;
} else {
// If we need to fold (i.e. if the window size is
// greater than the fft size so we are doing a
// time-aliased presum fft) or zero-pad, then we might
// as well use our standard function for it. This
2010-05-29 22:07:54 +01:00
// means we retain the m_afilter cut if folding as well,
2010-05-25 21:49:05 +01:00
// which is good for consistency with real-time mode.
// We get fftshift as well, which we don't want, but
// the penalty is nominal.
// Note that we can't do this in-place. Pity
2007-11-06 21:41:16 +00:00
2010-05-25 21:49:05 +01:00
float * tmp = ( float * ) alloca
( std : : max ( m_fftSize , m_aWindowSize ) * sizeof ( float ) ) ;
2007-11-06 21:41:16 +00:00
2010-05-25 21:49:05 +01:00
if ( m_aWindowSize > m_fftSize ) {
2010-05-29 22:07:54 +01:00
m_afilter - > cut ( cd . accumulator ) ;
2010-05-25 21:49:05 +01:00
}
cutShiftAndFold ( tmp , m_fftSize , cd . accumulator , m_awindow ) ;
v_copy ( cd . accumulator , tmp , m_fftSize ) ;
}
2007-11-06 21:41:16 +00:00
m_studyFFT - > forwardMagnitude ( cd . accumulator , cd . fltbuf ) ;
2009-09-17 13:01:21 +00:00
float df = m_phaseResetAudioCurve - > processFloat ( cd . fltbuf , m_increment ) ;
2007-11-19 20:13:39 +00:00
m_phaseResetDf . push_back ( df ) ;
2007-11-06 21:41:16 +00:00
2007-11-19 20:13:39 +00:00
// cout << m_phaseResetDf.size() << " [" << final << "] -> " << df << " \t: ";
2007-11-06 21:41:16 +00:00
2009-09-17 13:01:21 +00:00
df = m_stretchAudioCurve - > processFloat ( cd . fltbuf , m_increment ) ;
2007-11-06 21:41:16 +00:00
m_stretchDf . push_back ( df ) ;
2009-09-17 13:01:21 +00:00
df = m_silentAudioCurve - > processFloat ( cd . fltbuf , m_increment ) ;
2008-07-02 21:20:22 +00:00
bool silent = ( df > 0.f ) ;
2008-07-02 21:28:23 +00:00
if ( silent & & m_debugLevel > 1 ) {
cerr < < " silence found at " < < m_inputDuration < < endl ;
}
2008-07-02 21:20:22 +00:00
m_silence . push_back ( silent ) ;
2007-11-06 21:41:16 +00:00
// cout << df << endl;
2010-05-16 10:44:38 +01:00
// We have augmented the input by m_aWindowSize/2 so that
// the first chunk is centred on the first audio sample.
// We want to ensure that m_inputDuration contains the
// exact input duration without including this extra bit.
// We just add up all the increments here, and deduct the
// extra afterwards.
2007-11-06 21:41:16 +00:00
m_inputDuration + = m_increment ;
// cerr << "incr input duration by increment: " << m_increment << " -> " << m_inputDuration << endl;
inbuf . skip ( m_increment ) ;
}
}
if ( final ) {
int rs = inbuf . getReadSpace ( ) ;
m_inputDuration + = rs ;
// cerr << "incr input duration by read space: " << rs << " -> " << m_inputDuration << endl;
2010-05-16 10:44:38 +01:00
if ( m_inputDuration > m_aWindowSize / 2 ) { // deducting the extra
m_inputDuration - = m_aWindowSize / 2 ;
2007-11-06 21:41:16 +00:00
}
}
if ( m_channels > 1 ) delete [ ] mdalloc ;
}
vector < int >
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : getOutputIncrements ( ) const
2007-11-06 21:41:16 +00:00
{
if ( ! m_realtime ) {
return m_outputIncrements ;
} else {
vector < int > increments ;
while ( m_lastProcessOutputIncrements . getReadSpace ( ) > 0 ) {
increments . push_back ( m_lastProcessOutputIncrements . readOne ( ) ) ;
}
return increments ;
}
}
vector < float >
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : getPhaseResetCurve ( ) const
2007-11-06 21:41:16 +00:00
{
if ( ! m_realtime ) {
2007-11-19 20:13:39 +00:00
return m_phaseResetDf ;
2007-11-06 21:41:16 +00:00
} else {
vector < float > df ;
2007-11-19 20:13:39 +00:00
while ( m_lastProcessPhaseResetDf . getReadSpace ( ) > 0 ) {
df . push_back ( m_lastProcessPhaseResetDf . readOne ( ) ) ;
2007-11-06 21:41:16 +00:00
}
return df ;
}
}
2007-11-07 22:34:30 +00:00
vector < int >
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : getExactTimePoints ( ) const
2007-11-07 22:34:30 +00:00
{
std : : vector < int > points ;
if ( ! m_realtime ) {
std : : vector < StretchCalculator : : Peak > peaks =
m_stretchCalculator - > getLastCalculatedPeaks ( ) ;
for ( size_t i = 0 ; i < peaks . size ( ) ; + + i ) {
2007-11-18 21:38:18 +00:00
points . push_back ( peaks [ i ] . chunk ) ;
2007-11-07 22:34:30 +00:00
}
}
return points ;
}
2007-11-06 21:41:16 +00:00
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : calculateStretch ( )
2007-11-06 21:41:16 +00:00
{
2008-07-01 10:25:17 +00:00
Profiler profiler ( " RubberBandStretcher::Impl::calculateStretch " ) ;
2008-05-22 16:54:27 +00:00
2008-11-26 22:31:55 +00:00
size_t inputDuration = m_inputDuration ;
if ( ! m_realtime & & m_expectedInputDuration > 0 ) {
if ( m_expectedInputDuration ! = inputDuration ) {
std : : cerr < < " RubberBandStretcher: WARNING: Actual study() duration differs from duration set by setExpectedInputDuration ( " < < m_inputDuration < < " vs " < < m_expectedInputDuration < < " , diff = " < < ( m_expectedInputDuration - m_inputDuration ) < < " ), using the latter for calculation " < < std : : endl ;
inputDuration = m_expectedInputDuration ;
}
}
2010-03-24 09:44:51 +00:00
double prdm = 0 , sdm = 0 ;
if ( ! m_phaseResetDf . empty ( ) ) {
for ( int i = 0 ; i < m_phaseResetDf . size ( ) ; + + i ) prdm + = m_phaseResetDf [ i ] ;
prdm / = m_phaseResetDf . size ( ) ;
}
if ( ! m_stretchDf . empty ( ) ) {
for ( int i = 0 ; i < m_stretchDf . size ( ) ; + + i ) sdm + = m_stretchDf [ i ] ;
sdm / = m_stretchDf . size ( ) ;
}
// std::cerr << "phase reset df mean = " << prdm << ", stretch df mean = " << sdm << std::endl;
2007-11-06 21:41:16 +00:00
std : : vector < int > increments = m_stretchCalculator - > calculate
( getEffectiveRatio ( ) ,
2008-11-26 22:31:55 +00:00
inputDuration ,
2007-11-19 20:13:39 +00:00
m_phaseResetDf ,
2007-11-06 21:41:16 +00:00
m_stretchDf ) ;
2008-07-02 21:20:22 +00:00
int history = 0 ;
for ( size_t i = 0 ; i < increments . size ( ) ; + + i ) {
if ( i > = m_silence . size ( ) ) break ;
if ( m_silence [ i ] ) + + history ;
else history = 0 ;
2010-05-16 10:44:38 +01:00
if ( history > = int ( m_aWindowSize / m_increment ) & & increments [ i ] > = 0 ) {
2008-07-02 21:20:22 +00:00
increments [ i ] = - increments [ i ] ;
2008-07-02 21:28:23 +00:00
if ( m_debugLevel > 1 ) {
2008-07-02 21:20:22 +00:00
std : : cerr < < " phase reset on silence (silent history == "
< < history < < " ) " < < std : : endl ;
2008-07-02 21:28:23 +00:00
}
2008-07-02 21:20:22 +00:00
}
}
2007-11-06 21:41:16 +00:00
if ( m_outputIncrements . empty ( ) ) m_outputIncrements = increments ;
else {
for ( size_t i = 0 ; i < increments . size ( ) ; + + i ) {
m_outputIncrements . push_back ( increments [ i ] ) ;
}
}
return ;
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : setDebugLevel ( int level )
2007-11-06 21:41:16 +00:00
{
m_debugLevel = level ;
if ( m_stretchCalculator ) m_stretchCalculator - > setDebugLevel ( level ) ;
}
size_t
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : getSamplesRequired ( ) const
2007-11-06 21:41:16 +00:00
{
2008-07-01 10:25:17 +00:00
Profiler profiler ( " RubberBandStretcher::Impl::getSamplesRequired " ) ;
2008-05-22 16:54:27 +00:00
2007-11-06 21:41:16 +00:00
size_t reqd = 0 ;
for ( size_t c = 0 ; c < m_channels ; + + c ) {
size_t reqdHere = 0 ;
ChannelData & cd = * m_channelData [ c ] ;
RingBuffer < float > & inbuf = * cd . inbuf ;
2011-11-25 11:11:59 +00:00
RingBuffer < float > & outbuf = * cd . outbuf ;
2007-11-06 21:41:16 +00:00
size_t rs = inbuf . getReadSpace ( ) ;
2011-11-25 11:11:59 +00:00
size_t ws = outbuf . getReadSpace ( ) ;
if ( m_debugLevel > 2 ) {
cerr < < " getSamplesRequired: ws = " < < ws < < " , rs = " < < rs < < " , m_aWindowSize = " < < m_aWindowSize < < endl ;
}
// We should never return zero in non-threaded modes if
// available() would also return zero, i.e. if ws == 0. If we
// do that, nothing will ever happen again! We need to demand
// at least one increment (i.e. a nominal amount) to feed the
// engine.
if ( ws = = 0 & & reqd = = 0 ) reqd = m_increment ;
2007-11-06 21:41:16 +00:00
2007-12-10 22:20:07 +00:00
// See notes in testInbufReadSpace
2007-11-06 21:41:16 +00:00
2010-05-16 10:44:38 +01:00
if ( rs < m_aWindowSize & & ! cd . draining ) {
2007-11-06 21:41:16 +00:00
if ( cd . inputSize = = - 1 ) {
2010-05-16 10:44:38 +01:00
reqdHere = m_aWindowSize - rs ;
2007-11-06 21:41:16 +00:00
if ( reqdHere > reqd ) reqd = reqdHere ;
continue ;
}
if ( rs = = 0 ) {
2010-05-16 10:44:38 +01:00
reqdHere = m_aWindowSize ;
2007-11-06 21:41:16 +00:00
if ( reqdHere > reqd ) reqd = reqdHere ;
continue ;
}
}
}
return reqd ;
}
void
2008-07-01 10:25:17 +00:00
RubberBandStretcher : : Impl : : process ( const float * const * input , size_t samples , bool final )
2007-11-06 21:41:16 +00:00
{
2008-07-01 10:25:17 +00:00
Profiler profiler ( " RubberBandStretcher::Impl::process " ) ;
2008-05-22 16:54:27 +00:00
2007-11-06 21:41:16 +00:00
if ( m_mode = = Finished ) {
2008-07-01 10:25:17 +00:00
cerr < < " RubberBandStretcher::Impl::process: Cannot process again after final chunk " < < endl ;
2007-11-06 21:41:16 +00:00
return ;
}
if ( m_mode = = JustCreated | | m_mode = = Studying ) {
if ( m_mode = = Studying ) {
2011-11-25 11:11:59 +00:00
2007-11-06 21:41:16 +00:00
calculateStretch ( ) ;
2011-11-25 11:11:59 +00:00
if ( ! m_realtime ) {
// See note in configure() above. Of course, we should
// never enter Studying unless we are non-RT anyway
if ( m_debugLevel > 1 ) {
cerr < < " Not real time mode: prefilling " < < endl ;
}
for ( size_t c = 0 ; c < m_channels ; + + c ) {
m_channelData [ c ] - > reset ( ) ;
m_channelData [ c ] - > inbuf - > zero ( m_aWindowSize / 2 ) ;
}
}
2007-11-06 21:41:16 +00:00
}
2012-09-09 16:57:42 +01:00
# ifndef NO_THREADING
2007-11-06 21:41:16 +00:00
if ( m_threaded ) {
MutexLocker locker ( & m_threadSetMutex ) ;
for ( size_t c = 0 ; c < m_channels ; + + c ) {
ProcessThread * thread = new ProcessThread ( this , c ) ;
m_threadSet . insert ( thread ) ;
thread - > start ( ) ;
}
if ( m_debugLevel > 0 ) {
cerr < < m_channels < < " threads created " < < endl ;
}
}
2012-09-09 16:57:42 +01:00
# endif
2007-11-06 21:41:16 +00:00
m_mode = Processing ;
}
bool allConsumed = false ;
2008-07-01 14:54:31 +00:00
size_t * consumed = ( size_t * ) alloca ( m_channels * sizeof ( size_t ) ) ;
2007-11-06 21:41:16 +00:00
for ( size_t c = 0 ; c < m_channels ; + + c ) {
consumed [ c ] = 0 ;
}
while ( ! allConsumed ) {
// In a threaded mode, our "consumed" counters only indicate
// the number of samples that have been taken into the input
// ring buffers waiting to be processed by the process thread.
// In non-threaded mode, "consumed" counts the number that
// have actually been processed.
allConsumed = true ;
2008-05-22 16:54:27 +00:00
2007-11-06 21:41:16 +00:00
for ( size_t c = 0 ; c < m_channels ; + + c ) {
consumed [ c ] + = consumeChannel ( c ,
2011-11-25 11:11:59 +00:00
input ,
consumed [ c ] ,
2008-05-22 16:54:27 +00:00
samples - consumed [ c ] ,
final ) ;
2007-11-06 21:41:16 +00:00
if ( consumed [ c ] < samples ) {
allConsumed = false ;
// cerr << "process: waiting on input consumption for channel " << c << endl;
} else {
if ( final ) {
m_channelData [ c ] - > inputSize = m_channelData [ c ] - > inCount ;
}
// cerr << "process: happy with channel " << c << endl;
}
2011-12-09 18:18:32 +00:00
if (
2012-09-09 16:57:42 +01:00
# ifndef NO_THREADING
2011-12-09 18:18:32 +00:00
! m_threaded & &
2012-09-09 16:57:42 +01:00
# endif
2011-12-09 18:18:32 +00:00
! m_realtime ) {
2007-11-26 11:00:47 +00:00
bool any = false , last = false ;
processChunks ( c , any , last ) ;
2007-11-06 21:41:16 +00:00
}
}
if ( m_realtime ) {
// When running in real time, we need to process both
// channels in step because we will need to use the sum of
// their frequency domain representations as the input to
// the realtime onset detector
processOneChunk ( ) ;
}
2012-09-09 16:57:42 +01:00
# ifndef NO_THREADING
2007-11-06 21:41:16 +00:00
if ( m_threaded ) {
2007-11-26 21:51:05 +00:00
for ( ThreadSet : : iterator i = m_threadSet . begin ( ) ;
i ! = m_threadSet . end ( ) ; + + i ) {
( * i ) - > signalDataAvailable ( ) ;
}
2009-09-17 13:01:21 +00:00
m_spaceAvailable . lock ( ) ;
2007-11-06 21:41:16 +00:00
if ( ! allConsumed ) {
m_spaceAvailable . wait ( 500 ) ;
}
2009-09-17 13:01:21 +00:00
m_spaceAvailable . unlock ( ) ;
2007-11-06 21:41:16 +00:00
}
2012-09-09 16:57:42 +01:00
# endif
2008-07-01 20:49:24 +00:00
2011-11-25 11:11:59 +00:00
if ( m_debugLevel > 2 ) {
if ( ! allConsumed ) cerr < < " process looping " < < endl ;
}
}
2008-07-01 20:49:24 +00:00
2011-11-25 11:11:59 +00:00
if ( m_debugLevel > 2 ) {
cerr < < " process returning " < < endl ;
2007-11-06 21:41:16 +00:00
}
if ( final ) m_mode = Finished ;
}
}