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 .
2024-03-07 15:19:37 +00:00
Copyright 2007 - 2024 Particular Programs Ltd .
2012-09-09 16:57:42 +01:00
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
*/
2022-06-21 10:25:08 +01:00
# include "R2Stretcher.h"
2009-09-17 13:01:21 +00:00
2022-05-19 13:34:51 +01:00
# include "PercussiveAudioCurve.h"
# include "HighFrequencyAudioCurve.h"
# include "SilentAudioCurve.h"
# include "CompoundAudioCurve.h"
2007-11-06 21:41:16 +00:00
# include "StretcherChannelData.h"
2009-09-17 13:01:21 +00:00
2022-05-23 17:59:40 +01:00
# include "../common/StretchCalculator.h"
2022-05-19 13:34:51 +01:00
# include "../common/Resampler.h"
# include "../common/Profiler.h"
# include "../common/sysutils.h"
2022-09-26 16:02:13 +01:00
# include "../common/mathmisc.h"
2007-11-06 21:41:16 +00:00
# include <cassert>
# include <cmath>
# include <set>
# include <map>
2015-06-29 14:50:33 +01:00
# include <algorithm>
2007-11-06 21:41:16 +00:00
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
2022-06-21 10:25:08 +01:00
R2Stretcher : : m_defaultIncrement = 256 ;
2007-11-20 20:17:13 +00:00
const size_t
2022-06-21 10:25:08 +01:00
R2Stretcher : : m_defaultFftSize = 2048 ;
2007-11-20 20:17:13 +00:00
2022-06-21 10:25:08 +01:00
R2Stretcher : : R2Stretcher ( size_t sampleRate ,
size_t channels ,
RubberBandStretcher : : Options options ,
double initialTimeRatio ,
2022-06-21 16:06:16 +01:00
double initialPitchScale ,
Log log ) :
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 ) ,
2022-06-21 16:06:16 +01:00
m_log ( log ) ,
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 ) ,
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
{
2022-07-14 10:02:39 +01:00
Profiler profiler ( " R2Stretcher::R2Stretcher " ) ;
2008-05-22 16:54:27 +00:00
2022-06-22 13:42:58 +01:00
m_log . log ( 1 , " R2Stretcher::R2Stretcher: rate, options " ,
m_sampleRate , options ) ;
m_log . log ( 1 , " R2Stretcher::R2Stretcher: initial time ratio and pitch scale " ,
m_timeRatio , m_pitchScale ) ;
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
2022-06-21 10:25:08 +01:00
if ( ( options & RubberBandStretcher : : OptionWindowShort ) | |
( options & RubberBandStretcher : : OptionWindowLong ) ) {
if ( ( options & RubberBandStretcher : : OptionWindowShort ) & &
( options & RubberBandStretcher : : OptionWindowLong ) ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R2Stretcher::R2Stretcher: Cannot specify OptionWindowLong and OptionWindowShort together; falling back to OptionWindowStandard " ) ;
2022-06-21 10:25:08 +01:00
} else if ( options & RubberBandStretcher : : OptionWindowShort ) {
2010-05-16 10:44:38 +01:00
m_baseFftSize = m_baseFftSize / 2 ;
2022-06-22 09:10:02 +01:00
m_log . log ( 1 , " setting baseFftSize " , m_baseFftSize ) ;
2022-06-21 10:25:08 +01:00
} else if ( options & RubberBandStretcher : : OptionWindowLong ) {
2010-05-16 10:44:38 +01:00
m_baseFftSize = m_baseFftSize * 2 ;
2022-06-22 09:10:02 +01:00
m_log . log ( 1 , " setting baseFftSize " , m_baseFftSize ) ;
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
}
2022-06-21 10:25:08 +01:00
if ( m_options & RubberBandStretcher : : OptionProcessRealTime ) {
2007-11-06 21:41:16 +00:00
m_realtime = true ;
}
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 ;
2022-06-21 10:25:08 +01:00
} else if ( m_options & RubberBandStretcher : : OptionThreadingNever ) {
2007-11-26 11:50:29 +00:00
m_threaded = false ;
2022-06-21 10:25:08 +01:00
} else if ( ! ( m_options & RubberBandStretcher : : OptionThreadingAlways ) & &
2007-11-26 11:50:29 +00:00
! system_is_multiprocessor ( ) ) {
m_threaded = false ;
}
2007-11-06 21:41:16 +00:00
2022-06-21 20:26:25 +01:00
if ( m_threaded ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 1 , " Going multithreaded... " ) ;
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 ( ) ;
}
2022-06-21 10:25:08 +01:00
R2Stretcher : : ~ R2Stretcher ( )
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 ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 1 , " RubberBandStretcher::~RubberBandStretcher: joining for channel " , ( * i ) - > channel ( ) ) ;
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 ;
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
2022-06-21 10:25:08 +01:00
R2Stretcher : : 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 ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 1 , " RubberBandStretcher::~RubberBandStretcher: joining for channel " , ( * i ) - > channel ( ) ) ;
2007-12-10 15:47:06 +00:00
( * 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 ) {
2017-09-26 09:51:22 +01:00
m_stretchCalculator - > reset ( ) ;
2010-03-24 09:44:51 +00:00
}
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 ( ) ;
2008-07-02 21:20:22 +00:00
if ( m_silentAudioCurve ) m_silentAudioCurve - > reset ( ) ;
2023-03-23 17:26:11 +00:00
m_expectedInputDuration = 0 ;
m_maxProcessSize = 0 ;
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
2022-06-21 10:25:08 +01:00
R2Stretcher : : setTimeRatio ( double ratio )
2007-11-06 21:41:16 +00:00
{
if ( ! m_realtime ) {
if ( m_mode = = Studying | | m_mode = = Processing ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R2Stretcher::setTimeRatio: Cannot set ratio while studying or processing in non-RT mode " ) ;
2007-11-06 21:41:16 +00:00
return ;
}
}
if ( ratio = = m_timeRatio ) return ;
m_timeRatio = ratio ;
reconfigure ( ) ;
}
void
2022-06-21 10:25:08 +01:00
R2Stretcher : : setPitchScale ( double fs )
2007-11-06 21:41:16 +00:00
{
if ( ! m_realtime ) {
if ( m_mode = = Studying | | m_mode = = Processing ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R2Stretcher::setPitchScale: Cannot set ratio while studying or processing in non-RT mode " ) ;
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
2022-06-21 10:25:08 +01:00
if ( ! ( m_options & RubberBandStretcher : : OptionPitchHighConsistency ) & &
2008-07-04 14:19:32 +00:00
( 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
2022-06-21 10:25:08 +01:00
R2Stretcher : : getTimeRatio ( ) const
2007-11-06 21:41:16 +00:00
{
return m_timeRatio ;
}
double
2022-06-21 10:25:08 +01:00
R2Stretcher : : getPitchScale ( ) const
2007-11-06 21:41:16 +00:00
{
return m_pitchScale ;
}
void
2022-06-21 10:25:08 +01:00
R2Stretcher : : setExpectedInputDuration ( size_t samples )
2007-11-06 21:41:16 +00:00
{
if ( samples = = m_expectedInputDuration ) return ;
m_expectedInputDuration = samples ;
reconfigure ( ) ;
}
void
2022-06-21 10:25:08 +01:00
R2Stretcher : : setMaxProcessSize ( size_t samples )
2007-11-06 21:41:16 +00:00
{
2023-06-07 11:16:07 +01:00
m_log . log ( 2 , " R2Stretcher::setMaxProcessSize " , samples ) ;
2007-11-18 21:38:18 +00:00
if ( samples < = m_maxProcessSize ) return ;
2023-06-07 11:16:07 +01:00
m_log . log ( 2 , " R2Stretcher::setMaxProcessSize: increasing from, to " , m_maxProcessSize , samples ) ;
2007-11-18 21:38:18 +00:00
m_maxProcessSize = samples ;
2007-11-06 21:41:16 +00:00
reconfigure ( ) ;
}
2023-07-25 13:11:21 +01:00
size_t
R2Stretcher : : getProcessSizeLimit ( ) const
{
return 524288 ;
}
2010-03-24 09:44:51 +00:00
void
2022-08-04 16:58:00 +01:00
R2Stretcher : : setKeyFrameMap ( const std : : map < size_t , size_t > & mapping )
2010-03-24 09:44:51 +00:00
{
if ( m_realtime ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R2Stretcher::setKeyFrameMap: Cannot specify key frame map in RT mode " ) ;
2010-03-24 09:44:51 +00:00
return ;
}
if ( m_mode = = Processing ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R2Stretcher::setKeyFrameMap: Cannot specify key frame map after process() has begun " ) ;
2010-03-24 09:44:51 +00:00
return ;
}
if ( m_stretchCalculator ) {
m_stretchCalculator - > setKeyFrameMap ( mapping ) ;
}
}
2007-11-06 21:41:16 +00:00
float
2022-06-21 10:25:08 +01:00
R2Stretcher : : 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
2022-06-21 10:25:08 +01:00
R2Stretcher : : 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
2022-06-21 10:25:08 +01:00
R2Stretcher : : 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 ;
}
void
2022-06-21 10:25:08 +01:00
R2Stretcher : : 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
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " WARNING: Pitch scale must be greater than zero! Resetting it to default, no pitch shift will happen " , m_pitchScale ) ;
2008-10-13 19:13:21 +00:00
m_pitchScale = 1.0 ;
}
if ( m_timeRatio < = 0.0 ) {
// Likewise
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " WARNING: Time ratio must be greater than zero! Resetting it to default, no time stretch will happen " , m_timeRatio ) ;
2008-10-13 19:13:21 +00:00
m_timeRatio = 1.0 ;
}
2022-09-30 10:36:29 +01:00
if ( m_pitchScale ! = m_pitchScale | | m_timeRatio ! = m_timeRatio | |
m_pitchScale = = m_pitchScale / 2.0 | | m_timeRatio = = m_timeRatio / 2.0 ) {
m_log . log ( 0 , " WARNING: NaN or Inf presented for time ratio or pitch scale! Resetting it to default, no time stretch will happen " , m_timeRatio , m_pitchScale ) ;
m_timeRatio = 1.0 ;
m_pitchScale = 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 ) ;
}
2020-09-15 15:23:33 +01:00
while ( 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 ) {
2022-06-22 11:33:36 +01:00
size_t oldWindowSize = windowSize ;
2008-05-22 16:54:27 +00:00
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 ;
}
2022-06-22 11:33:36 +01:00
m_log . log ( 2 , " adjusting window size from/to " , oldWindowSize , windowSize ) ;
m_log . log ( 2 , " input and output increments " , inputIncrement , outputIncrement ) ;
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 ) ;
}
2020-09-15 15:23:33 +01:00
while ( 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
2022-06-21 10:25:08 +01:00
if ( m_options & RubberBandStretcher : : OptionSmoothingOn ) {
2010-05-29 22:07:54 +01:00
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
2022-06-22 09:10:02 +01:00
m_log . log ( 1 , " calculateSizes: time ratio and pitch scale " , m_timeRatio , m_pitchScale ) ;
m_log . log ( 1 , " effective ratio " , getEffectiveRatio ( ) ) ;
m_log . log ( 1 , " analysis and synthesis window sizes " , m_aWindowSize , m_sWindowSize ) ;
m_log . log ( 1 , " fft size " , m_fftSize ) ;
m_log . log ( 1 , " input increment and mean output increment " , m_increment , m_increment * getEffectiveRatio ( ) ) ;
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
}
2022-06-22 11:33:36 +01:00
m_log . log ( 1 , " calculateSizes: outbuf size " , m_outbufSize ) ;
2007-11-06 21:41:16 +00:00
}
void
2022-06-21 10:25:08 +01:00
R2Stretcher : : configure ( )
2007-11-06 21:41:16 +00:00
{
2022-06-22 11:33:36 +01:00
if ( m_realtime ) {
m_log . log ( 1 , " configure, realtime: pitch scale and channels " ,
m_pitchScale , m_channels ) ;
} else {
m_log . log ( 1 , " configure, offline: pitch scale and channels " ,
m_pitchScale , m_channels ) ;
}
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 ( ) ) {
2022-05-18 17:51:20 +01:00
m_windows [ * i ] = new Window < float > ( HannWindow , * i ) ;
2007-11-06 21:41:16 +00:00
}
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
2022-06-22 11:33:36 +01:00
m_log . log ( 1 , " analysis and synthesis window areas " ,
m_awindow - > getArea ( ) , m_swindow - > getArea ( ) ) ;
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 ;
2022-08-30 18:55:33 +01:00
m_studyFFT = new FFT ( m_fftSize ) ;
2007-11-06 21:41:16 +00:00
m_studyFFT - > initFloat ( ) ;
}
2008-07-01 20:49:24 +00:00
if ( m_pitchScale ! = 1.0 | |
2022-06-21 10:25:08 +01:00
( m_options & RubberBandStretcher : : OptionPitchHighConsistency ) | |
2008-07-01 20:49:24 +00:00
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 ;
2020-10-21 15:05:14 +01:00
Resampler : : Parameters params ;
params . quality = Resampler : : FastestTolerable ;
2021-05-12 17:29:26 +01:00
if ( m_realtime ) {
params . dynamism = Resampler : : RatioOftenChanging ;
params . ratioChange = Resampler : : SmoothRatioChange ;
} else {
// ratio can't be changed in offline mode
params . dynamism = Resampler : : RatioMostlyFixed ;
params . ratioChange = Resampler : : SuddenRatioChange ;
}
2020-10-21 15:05:14 +01:00
params . maxBufferSize = 4096 * 16 ;
2022-06-22 13:42:58 +01:00
int myLevel = m_log . getDebugLevel ( ) ;
params . debugLevel = ( myLevel > 0 ? myLevel - 1 : 0 ) ;
2020-10-21 15:05:14 +01:00
m_channelData [ c ] - > resampler = new Resampler ( params , 1 ) ;
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 ;
2023-02-20 15:26:12 +00:00
if ( rbs < m_aWindowSize * 2 ) rbs = m_aWindowSize * 2 ;
2008-05-22 16:54:27 +00:00
m_channelData [ c ] - > setResampleBufSize ( rbs ) ;
2007-11-06 21:41:16 +00:00
}
}
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
delete m_stretchCalculator ;
m_stretchCalculator = new StretchCalculator
2008-06-09 20:46:37 +00:00
( m_sampleRate , m_increment ,
2022-06-21 16:06:16 +01:00
! ( m_options & RubberBandStretcher : : OptionTransientsSmooth ) ,
m_log ) ;
2007-11-06 21:41:16 +00:00
2022-06-22 13:42:58 +01:00
m_stretchCalculator - > setDebugLevel ( m_log . getDebugLevel ( ) ) ;
2007-11-06 21:41:16 +00:00
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.
2021-05-14 15:10:24 +01:00
if ( ! m_realtime ) {
2022-06-22 11:33:36 +01:00
m_log . log ( 1 , " offline mode: prefilling with " , m_aWindowSize / 2 ) ;
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
}
2022-06-22 11:33:36 +01:00
} else {
m_log . log ( 1 , " realtime mode: no prefill " ) ;
2021-05-14 15:10:24 +01:00
}
2007-11-06 21:41:16 +00:00
}
void
2022-06-21 10:25:08 +01:00
R2Stretcher : : 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 ( ) ;
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 ( ) ;
2021-05-13 18:04:43 +01:00
bool somethingChanged = false ;
2007-11-06 21:41:16 +00:00
// 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 ( ) ) {
2022-06-22 11:33:36 +01:00
m_log . log ( 0 , " WARNING: reconfigure(): window allocation required in realtime mode, size " , m_aWindowSize ) ;
2010-05-16 10:44:38 +01:00
m_windows [ m_aWindowSize ] = new Window < float >
2022-05-18 17:51:20 +01:00
( HannWindow , 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 ( ) ) {
2022-06-22 11:33:36 +01:00
m_log . log ( 0 , " WARNING: reconfigure(): window allocation required in realtime mode, size " , m_sWindowSize ) ;
2010-05-16 10:44:38 +01:00
m_windows [ m_sWindowSize ] = new Window < float >
2022-05-18 17:51:20 +01:00
( HannWindow , 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
}
2021-05-13 18:04:43 +01:00
somethingChanged = true ;
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 ) ;
}
2021-05-13 18:04:43 +01:00
somethingChanged = true ;
2007-11-06 21:41:16 +00:00
}
if ( m_pitchScale ! = 1.0 ) {
for ( size_t c = 0 ; c < m_channels ; + + c ) {
if ( m_channelData [ c ] - > resampler ) continue ;
2022-06-22 11:33:36 +01:00
m_log . log ( 0 , " WARNING: reconfigure(): resampler construction required in RT mode " ) ;
2007-11-06 21:41:16 +00:00
2020-10-21 15:05:14 +01:00
Resampler : : Parameters params ;
params . quality = Resampler : : FastestTolerable ;
2021-05-12 17:29:26 +01:00
params . dynamism = Resampler : : RatioOftenChanging ;
2021-05-11 17:20:12 +01:00
params . ratioChange = Resampler : : SmoothRatioChange ;
2020-10-21 15:05:14 +01:00
params . maxBufferSize = m_sWindowSize ;
2022-06-22 13:42:58 +01:00
int myLevel = m_log . getDebugLevel ( ) ;
params . debugLevel = ( myLevel > 0 ? myLevel - 1 : 0 ) ;
2020-10-21 15:05:14 +01:00
m_channelData [ c ] - > resampler = new Resampler ( params , 1 ) ;
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 ) ;
2021-05-13 18:04:43 +01:00
somethingChanged = true ;
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 ) ;
2021-06-01 08:49:54 +01:00
m_silentAudioCurve - > setFftSize ( m_fftSize ) ;
2021-05-13 18:04:43 +01:00
somethingChanged = true ;
}
2022-06-22 11:33:36 +01:00
if ( somethingChanged ) {
m_log . log ( 1 , " reconfigure: at least one parameter changed " ) ;
} else {
m_log . log ( 1 , " reconfigure: nothing changed " ) ;
2007-11-06 21:41:16 +00:00
}
}
size_t
2022-07-05 17:53:36 +01:00
R2Stretcher : : getPreferredStartPad ( ) const
{
if ( ! m_realtime ) return 0 ;
2023-02-20 15:26:12 +00:00
size_t pad = m_aWindowSize / 2 ;
if ( resampleBeforeStretching ( ) ) {
return size_t ( ceil ( pad * m_pitchScale ) ) ;
} else {
return pad ;
}
2022-07-05 17:53:36 +01:00
}
size_t
R2Stretcher : : getStartDelay ( ) const
2007-11-06 21:41:16 +00:00
{
if ( ! m_realtime ) return 0 ;
2023-02-20 15:26:12 +00:00
size_t pad = m_aWindowSize / 2 ;
if ( resampleBeforeStretching ( ) ) {
return pad ;
} else {
return size_t ( ceil ( pad / m_pitchScale ) ) ;
}
2007-11-06 21:41:16 +00:00
}
void
2022-06-21 10:25:08 +01:00
R2Stretcher : : setTransientsOption ( RubberBandStretcher : : Options options )
2007-11-06 21:41:16 +00:00
{
2007-11-21 19:57:49 +00:00
if ( ! m_realtime ) {
2022-06-22 11:33:36 +01:00
m_log . log ( 0 , " R2Stretcher::setTransientsOption: Not permissible in non-realtime mode " ) ;
2007-11-21 19:57:49 +00:00
return ;
}
2022-06-21 10:25:08 +01:00
int mask = ( RubberBandStretcher : : OptionTransientsMixed |
RubberBandStretcher : : OptionTransientsSmooth |
RubberBandStretcher : : 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
2022-06-21 10:25:08 +01:00
( ! ( m_options & RubberBandStretcher : : OptionTransientsSmooth ) ) ;
2007-11-06 21:41:16 +00:00
}
2010-03-24 09:44:51 +00:00
void
2022-06-21 10:25:08 +01:00
R2Stretcher : : setDetectorOption ( RubberBandStretcher : : Options options )
2010-03-24 09:44:51 +00:00
{
if ( ! m_realtime ) {
2022-06-22 11:33:36 +01:00
m_log . log ( 0 , " R2Stretcher::setDetectorOption: Not permissible in non-realtime mode " ) ;
2010-03-24 09:44:51 +00:00
return ;
}
2022-06-21 10:25:08 +01:00
int mask = ( RubberBandStretcher : : OptionDetectorPercussive |
RubberBandStretcher : : OptionDetectorCompound |
RubberBandStretcher : : OptionDetectorSoft ) ;
2010-03-24 09:44:51 +00:00
m_options & = ~ mask ;
options & = mask ;
m_options | = options ;
CompoundAudioCurve : : Type dt = CompoundAudioCurve : : CompoundDetector ;
2022-06-21 10:25:08 +01:00
if ( m_options & RubberBandStretcher : : OptionDetectorPercussive ) {
dt = CompoundAudioCurve : : PercussiveDetector ;
} else if ( m_options & RubberBandStretcher : : OptionDetectorSoft ) {
dt = CompoundAudioCurve : : SoftDetector ;
}
2010-03-24 09:44:51 +00:00
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
2022-06-21 10:25:08 +01:00
R2Stretcher : : setPhaseOption ( RubberBandStretcher : : Options options )
2007-11-06 21:41:16 +00:00
{
2022-06-21 10:25:08 +01:00
int mask = ( RubberBandStretcher : : OptionPhaseLaminar |
RubberBandStretcher : : OptionPhaseIndependent ) ;
2008-05-22 16:54:27 +00:00
m_options & = ~ mask ;
options & = mask ;
m_options | = options ;
}
void
2022-06-21 10:25:08 +01:00
R2Stretcher : : setFormantOption ( RubberBandStretcher : : Options options )
2008-05-22 16:54:27 +00:00
{
2022-06-21 10:25:08 +01:00
int mask = ( RubberBandStretcher : : OptionFormantShifted |
RubberBandStretcher : : OptionFormantPreserved ) ;
2008-05-22 16:54:27 +00:00
m_options & = ~ mask ;
options & = mask ;
m_options | = options ;
}
void
2022-06-21 10:25:08 +01:00
R2Stretcher : : setPitchOption ( RubberBandStretcher : : Options options )
2008-05-22 16:54:27 +00:00
{
if ( ! m_realtime ) {
2022-06-22 11:33:36 +01:00
m_log . log ( 0 , " R2Stretcher::setPitchOption: Pitch option is not used in non-RT mode " ) ;
2008-05-22 16:54:27 +00:00
return ;
}
2022-06-21 10:25:08 +01:00
RubberBandStretcher : : Options prior = m_options ;
2008-05-22 16:54:27 +00:00
2022-06-21 10:25:08 +01:00
int mask = ( RubberBandStretcher : : OptionPitchHighQuality |
RubberBandStretcher : : OptionPitchHighSpeed |
RubberBandStretcher : : 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
2022-06-21 10:25:08 +01:00
R2Stretcher : : study ( const float * const * input , size_t samples , bool final )
2007-11-06 21:41:16 +00:00
{
2022-06-21 10:25:08 +01:00
Profiler profiler ( " R2Stretcher::study " ) ;
2008-05-22 16:54:27 +00:00
2007-11-06 21:41:16 +00:00
if ( m_realtime ) {
2022-06-22 11:33:36 +01:00
m_log . log ( 0 , " R2Stretcher::study: Not meaningful in realtime mode " ) ;
2007-11-06 21:41:16 +00:00
return ;
}
if ( m_mode = = Processing | | m_mode = = Finished ) {
2022-06-22 11:33:36 +01:00
m_log . log ( 0 , " R2Stretcher::study: Cannot study after processing " ) ;
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
2022-06-22 11:33:36 +01:00
m_log . log ( 0 , " WARNING: writable == 0: consumed, samples " ,
consumed , samples ) ;
2007-11-06 21:41:16 +00:00
} 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_silentAudioCurve - > processFloat ( cd . fltbuf , m_increment ) ;
2008-07-02 21:20:22 +00:00
bool silent = ( df > 0.f ) ;
2022-06-22 11:33:36 +01:00
if ( silent ) {
m_log . log ( 2 , " silence at " , m_inputDuration ) ;
2008-07-02 21:28:23 +00:00
}
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 ;
inbuf . skip ( m_increment ) ;
}
}
if ( final ) {
int rs = inbuf . getReadSpace ( ) ;
m_inputDuration + = rs ;
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
}
}
2014-10-16 21:36:39 +01:00
if ( m_channels > 1 | | final ) delete [ ] mdalloc ;
2007-11-06 21:41:16 +00:00
}
vector < int >
2022-06-21 10:25:08 +01:00
R2Stretcher : : 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 >
2022-06-21 10:25:08 +01:00
R2Stretcher : : 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 >
2022-06-21 10:25:08 +01:00
R2Stretcher : : 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
2022-06-21 10:25:08 +01:00
R2Stretcher : : calculateStretch ( )
2007-11-06 21:41:16 +00:00
{
2022-06-21 10:25:08 +01:00
Profiler profiler ( " R2Stretcher::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 ) {
2022-06-22 11:33:36 +01:00
m_log . log ( 0 , " WARNING: Actual study() duration differs from duration set by setExpectedInputDuration - using the latter for calculation " , m_inputDuration , m_expectedInputDuration ) ;
2008-11-26 22:31:55 +00:00
inputDuration = m_expectedInputDuration ;
}
}
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 ,
2022-05-18 14:12:57 +01:00
m_phaseResetDf ) ;
2007-11-06 21:41:16 +00:00
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 ] ;
2022-06-22 11:33:36 +01:00
m_log . log ( 2 , " phase reset on silence: silent history " , history ) ;
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
2022-06-21 10:25:08 +01:00
R2Stretcher : : setDebugLevel ( int level )
2007-11-06 21:41:16 +00:00
{
2022-06-22 13:42:58 +01:00
m_log . setDebugLevel ( level ) ;
if ( m_stretchCalculator ) {
m_stretchCalculator - > setDebugLevel ( level ) ;
}
2007-11-06 21:41:16 +00:00
}
size_t
2022-06-21 10:25:08 +01:00
R2Stretcher : : getSamplesRequired ( ) const
2007-11-06 21:41:16 +00:00
{
2022-06-21 10:25:08 +01:00
Profiler profiler ( " R2Stretcher::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 ( ) ;
2022-06-22 11:33:36 +01:00
m_log . log ( 3 , " getSamplesRequired: ws and rs " , ws , rs ) ;
2011-11-25 11:11:59 +00:00
// 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 ;
}
}
}
2023-02-20 15:26:12 +00:00
if ( resampleBeforeStretching ( ) & & m_pitchScale > 1.0 ) {
reqd = size_t ( ceil ( double ( reqd ) * m_pitchScale ) ) ;
}
2007-11-06 21:41:16 +00:00
return reqd ;
}
void
2022-06-21 10:25:08 +01:00
R2Stretcher : : process ( const float * const * input , size_t samples , bool final )
2007-11-06 21:41:16 +00:00
{
2022-06-21 10:25:08 +01:00
Profiler profiler ( " R2Stretcher::process " ) ;
2008-05-22 16:54:27 +00:00
2023-06-08 09:40:11 +01:00
m_log . log ( 3 , " process entering, samples and final " , samples , final ) ;
2023-06-07 11:16:07 +01:00
2007-11-06 21:41:16 +00:00
if ( m_mode = = Finished ) {
2022-06-22 11:33:36 +01:00
m_log . log ( 0 , " R2Stretcher::process: Cannot process again after final chunk " ) ;
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
2022-06-22 11:33:36 +01:00
m_log . log ( 1 , " offline mode: prefilling with " , m_aWindowSize / 2 ) ;
2011-11-25 11:11:59 +00:00
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 ( ) ;
}
2022-06-22 11:33:36 +01:00
m_log . log ( 1 , " created threads " , m_channels ) ;
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
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 ) ;
2023-06-07 11:16:07 +01:00
if ( c = = 0 ) {
2023-06-08 09:40:11 +01:00
m_log . log ( 3 , " consumed channel 0, consumed and samples now " , consumed [ c ] , samples ) ;
2023-06-07 11:16:07 +01:00
}
2007-11-06 21:41:16 +00:00
if ( consumed [ c ] < samples ) {
allConsumed = false ;
} else {
if ( final ) {
2023-06-07 11:16:07 +01:00
if ( c = = 0 ) {
m_log . log ( 2 , " final is true, setting input size " , m_channelData [ c ] - > inCount ) ;
}
2007-11-06 21:41:16 +00:00
m_channelData [ c ] - > inputSize = m_channelData [ c ] - > inCount ;
}
}
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
2023-06-08 09:40:11 +01:00
m_log . log ( 3 , " process looping " ) ;
2011-11-25 11:11:59 +00:00
}
2008-07-01 20:49:24 +00:00
2023-06-08 09:40:11 +01:00
m_log . log ( 3 , " process returning " ) ;
2007-11-06 21:41:16 +00:00
if ( final ) m_mode = Finished ;
}
}