2022-05-23 15:04:34 +01:00
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time - stretching and pitch - shifting library .
Copyright 2007 - 2022 Particular Programs Ltd .
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 .
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 .
*/
2022-06-21 10:25:08 +01:00
# include "R3Stretcher.h"
2022-05-23 15:04:34 +01:00
2022-05-24 17:35:23 +01:00
# include "../common/VectorOpsComplex.h"
2022-05-24 16:54:05 +01:00
2022-05-23 15:04:34 +01:00
# include <array>
namespace RubberBand {
2022-06-21 10:25:08 +01:00
R3Stretcher : : R3Stretcher ( Parameters parameters ,
double initialTimeRatio ,
2022-06-21 16:06:16 +01:00
double initialPitchScale ,
Log log ) :
2022-05-25 13:47:40 +01:00
m_parameters ( parameters ) ,
2022-06-21 16:06:16 +01:00
m_log ( log ) ,
2022-05-25 13:47:40 +01:00
m_timeRatio ( initialTimeRatio ) ,
m_pitchScale ( initialPitchScale ) ,
2022-06-13 10:39:13 +01:00
m_formantScale ( 0.0 ) ,
2022-06-21 16:06:16 +01:00
m_guide ( Guide : : Parameters ( m_parameters . sampleRate ) , m_log ) ,
2022-05-25 13:47:40 +01:00
m_guideConfiguration ( m_guide . getConfiguration ( ) ) ,
m_channelAssembly ( m_parameters . channels ) ,
m_inhop ( 1 ) ,
2022-06-17 15:01:26 +01:00
m_prevInhop ( 1 ) ,
2022-05-25 13:47:40 +01:00
m_prevOuthop ( 1 ) ,
2022-06-17 15:01:26 +01:00
m_unityCount ( 0 ) ,
2022-06-10 20:26:37 +01:00
m_startSkip ( 0 ) ,
2022-06-13 16:06:21 +01:00
m_studyInputDuration ( 0 ) ,
m_totalTargetDuration ( 0 ) ,
m_processInputDuration ( 0 ) ,
2022-06-17 17:52:28 +01:00
m_lastKeyFrameSurpassed ( 0 ) ,
2022-06-13 16:06:21 +01:00
m_totalOutputDuration ( 0 ) ,
m_mode ( ProcessMode : : JustCreated )
2022-05-25 13:47:40 +01:00
{
2022-06-07 11:05:50 +01:00
double maxClassifierFrequency = 16000.0 ;
if ( maxClassifierFrequency > m_parameters . sampleRate / 2 ) {
maxClassifierFrequency = m_parameters . sampleRate / 2 ;
}
int classificationBins =
int ( floor ( m_guideConfiguration . classificationFftSize *
maxClassifierFrequency / m_parameters . sampleRate ) ) ;
2022-05-25 13:47:40 +01:00
BinSegmenter : : Parameters segmenterParameters
( m_guideConfiguration . classificationFftSize ,
2022-06-14 13:59:17 +01:00
classificationBins , m_parameters . sampleRate , 18 ) ;
2022-06-07 11:05:50 +01:00
2022-05-25 13:47:40 +01:00
BinClassifier : : Parameters classifierParameters
2022-06-14 13:59:17 +01:00
( classificationBins , 9 , 1 , 10 , 2.0 , 2.0 ) ;
2022-05-25 13:47:40 +01:00
int inRingBufferSize = m_guideConfiguration . longestFftSize * 2 ;
int outRingBufferSize = m_guideConfiguration . longestFftSize * 16 ;
for ( int c = 0 ; c < m_parameters . channels ; + + c ) {
m_channelData . push_back ( std : : make_shared < ChannelData >
( segmenterParameters ,
classifierParameters ,
m_guideConfiguration . longestFftSize ,
inRingBufferSize ,
outRingBufferSize ) ) ;
for ( auto band : m_guideConfiguration . fftBandLimits ) {
int fftSize = band . fftSize ;
m_channelData [ c ] - > scales [ fftSize ] =
std : : make_shared < ChannelScaleData >
( fftSize , m_guideConfiguration . longestFftSize ) ;
}
}
2022-06-21 16:06:16 +01:00
2022-05-25 13:47:40 +01:00
for ( auto band : m_guideConfiguration . fftBandLimits ) {
int fftSize = band . fftSize ;
GuidedPhaseAdvance : : Parameters guidedParameters
2022-06-21 16:06:16 +01:00
( fftSize , m_parameters . sampleRate , m_parameters . channels ) ;
2022-06-21 17:03:24 +01:00
m_scaleData [ fftSize ] = std : : make_shared < ScaleData >
( guidedParameters , m_log ) ;
2022-05-25 13:47:40 +01:00
}
m_calculator = std : : unique_ptr < StretchCalculator >
( new StretchCalculator ( int ( round ( m_parameters . sampleRate ) ) , //!!! which is a double...
2022-06-21 16:06:16 +01:00
1 , false , // no fixed inputIncrement
m_log ) ) ;
2022-05-25 13:47:40 +01:00
Resampler : : Parameters resamplerParameters ;
resamplerParameters . quality = Resampler : : FastestTolerable ;
2022-06-07 09:06:52 +01:00
2022-06-13 16:06:21 +01:00
if ( isRealTime ( ) ) {
2022-06-07 09:06:52 +01:00
resamplerParameters . dynamism = Resampler : : RatioOftenChanging ;
resamplerParameters . ratioChange = Resampler : : SmoothRatioChange ;
} else {
// ratio can't be changed in offline mode
resamplerParameters . dynamism = Resampler : : RatioMostlyFixed ;
resamplerParameters . ratioChange = Resampler : : SuddenRatioChange ;
}
2022-05-25 13:47:40 +01:00
resamplerParameters . initialSampleRate = m_parameters . sampleRate ;
resamplerParameters . maxBufferSize = m_guideConfiguration . longestFftSize ; //!!!???
m_resampler = std : : unique_ptr < Resampler >
( new Resampler ( resamplerParameters , m_parameters . channels ) ) ;
2022-05-27 14:58:42 +01:00
2022-05-25 13:47:40 +01:00
calculateHop ( ) ;
2022-05-25 13:51:23 +01:00
2022-05-26 15:08:07 +01:00
m_prevInhop = m_inhop ;
2022-05-25 13:51:23 +01:00
m_prevOuthop = int ( round ( m_inhop * getEffectiveRatio ( ) ) ) ;
2022-05-25 14:10:41 +01:00
if ( ! m_inhop . is_lock_free ( ) ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " WARNING: std::atomic<int> is not lock-free " ) ;
2022-05-25 14:10:41 +01:00
}
if ( ! m_timeRatio . is_lock_free ( ) ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " WARNING: std::atomic<double> is not lock-free " ) ;
2022-05-25 14:10:41 +01:00
}
2022-06-10 18:15:58 +01:00
2022-06-10 20:26:37 +01:00
// Pad to half of the longest frame. As with R2, in real-time mode
// we don't do this -- it's better to start with a swoosh than
// introduce more latency, and we don't want gaps when the ratio
// changes.
2022-06-10 18:15:58 +01:00
2022-06-13 16:06:21 +01:00
if ( ! isRealTime ( ) ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 1 , " Offline mode: pre-padding " ) ;
2022-06-10 20:26:37 +01:00
int pad = m_guideConfiguration . longestFftSize / 2 ;
2022-06-10 18:15:58 +01:00
for ( int c = 0 ; c < m_parameters . channels ; + + c ) {
2022-06-10 20:26:37 +01:00
m_channelData [ c ] - > inbuf - > zero ( pad ) ;
2022-06-10 18:15:58 +01:00
}
2022-06-10 20:26:37 +01:00
// By the time we skip this later we will have resampled
m_startSkip = int ( round ( pad / m_pitchScale ) ) ;
2022-06-13 17:16:03 +01:00
} else {
2022-06-22 09:10:02 +01:00
m_log . log ( 1 , " RT mode: no internal pre-pad " ) ;
2022-06-10 18:15:58 +01:00
}
2022-05-25 13:47:40 +01:00
}
2022-05-26 15:08:07 +01:00
WindowType
2022-06-21 10:25:08 +01:00
R3Stretcher : : ScaleData : : analysisWindowShape ( int fftSize )
2022-05-26 15:08:07 +01:00
{
2022-06-10 18:15:58 +01:00
if ( fftSize > 2048 ) return HannWindow ;
2022-05-26 15:08:07 +01:00
else return NiemitaloForwardWindow ;
}
int
2022-06-21 10:25:08 +01:00
R3Stretcher : : ScaleData : : analysisWindowLength ( int fftSize )
2022-05-26 15:08:07 +01:00
{
return fftSize ;
}
WindowType
2022-06-21 10:25:08 +01:00
R3Stretcher : : ScaleData : : synthesisWindowShape ( int fftSize )
2022-05-26 15:08:07 +01:00
{
2022-06-10 18:15:58 +01:00
if ( fftSize > 2048 ) return HannWindow ;
2022-05-26 15:08:07 +01:00
else return NiemitaloReverseWindow ;
}
int
2022-06-21 10:25:08 +01:00
R3Stretcher : : ScaleData : : synthesisWindowLength ( int fftSize )
2022-05-26 15:08:07 +01:00
{
2022-06-10 20:48:17 +01:00
if ( fftSize > 2048 ) return fftSize / 2 ;
2022-05-26 15:08:07 +01:00
else return fftSize ;
}
2022-05-23 15:04:34 +01:00
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : setTimeRatio ( double ratio )
2022-05-23 15:04:34 +01:00
{
2022-06-13 16:06:21 +01:00
if ( ! isRealTime ( ) ) {
if ( m_mode = = ProcessMode : : Studying | |
m_mode = = ProcessMode : : Processing ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R3Stretcher::setTimeRatio: Cannot set time ratio while studying or processing in non-RT mode " ) ;
2022-06-13 16:06:21 +01:00
return ;
}
}
if ( ratio = = m_timeRatio ) return ;
2022-05-23 15:04:34 +01:00
m_timeRatio = ratio ;
2022-05-23 17:59:40 +01:00
calculateHop ( ) ;
2022-05-23 15:04:34 +01:00
}
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : setPitchScale ( double scale )
2022-05-23 15:04:34 +01:00
{
2022-06-13 16:06:21 +01:00
if ( ! isRealTime ( ) ) {
if ( m_mode = = ProcessMode : : Studying | |
m_mode = = ProcessMode : : Processing ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R3Stretcher::setTimeRatio: Cannot set pitch scale while studying or processing in non-RT mode " ) ;
2022-06-13 16:06:21 +01:00
return ;
}
}
if ( scale = = m_pitchScale ) return ;
2022-05-23 15:04:34 +01:00
m_pitchScale = scale ;
2022-05-23 17:59:40 +01:00
calculateHop ( ) ;
}
2022-06-13 10:39:13 +01:00
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : setFormantScale ( double scale )
2022-06-13 10:39:13 +01:00
{
2022-06-13 16:06:21 +01:00
if ( ! isRealTime ( ) ) {
if ( m_mode = = ProcessMode : : Studying | |
m_mode = = ProcessMode : : Processing ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R3Stretcher::setTimeRatio: Cannot set formant scale while studying or processing in non-RT mode " ) ;
2022-06-13 16:06:21 +01:00
return ;
}
}
2022-06-13 10:39:13 +01:00
m_formantScale = scale ;
}
2022-05-27 14:58:42 +01:00
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : setFormantOption ( RubberBandStretcher : : Options options )
2022-05-27 14:58:42 +01:00
{
int mask = ( RubberBandStretcher : : OptionFormantShifted |
RubberBandStretcher : : OptionFormantPreserved ) ;
m_parameters . options & = ~ mask ;
options & = mask ;
m_parameters . options | = options ;
}
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : setKeyFrameMap ( const std : : map < size_t , size_t > & mapping )
2022-05-27 14:58:42 +01:00
{
2022-06-13 16:06:21 +01:00
if ( isRealTime ( ) ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R3Stretcher::setKeyFrameMap: Cannot specify key frame map in RT mode " ) ;
2022-06-13 16:06:21 +01:00
return ;
}
if ( m_mode = = ProcessMode : : Processing | | m_mode = = ProcessMode : : Finished ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R3Stretcher::setKeyFrameMap: Cannot specify key frame map after process() has begun " ) ;
2022-06-13 16:06:21 +01:00
return ;
}
m_keyFrameMap = mapping ;
2022-05-27 14:58:42 +01:00
}
2022-05-23 17:59:40 +01:00
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : calculateHop ( )
2022-05-23 17:59:40 +01:00
{
double ratio = getEffectiveRatio ( ) ;
2022-06-14 15:01:44 +01:00
// In R2 we generally targeted a certain inhop, and calculated
// outhop from that. Here we are the other way around. We aim for
// outhop = 256 at ratios around 1, reducing down to 128 for
// ratios far below 1 and up to 512 for ratios far above. As soon
// as outhop exceeds 256 we have to drop the 1024-bin FFT, as the
2022-06-15 09:40:09 +01:00
// overlap will be inadequate for it (that's among the jobs of the
// Guide class) so we don't want to go above 256 until at least
// factor 1.5. Also we can't go above 512 without changing the
// window shape or dropping the 2048-bin FFT, and we can't do
// either of those dynamically.
double proposedOuthop = 256.0 ;
if ( ratio > 1.5 ) {
proposedOuthop = pow ( 2.0 , 8.0 + 2.0 * log10 ( ratio - 0.5 ) ) ;
} else if ( ratio < 1.0 ) {
proposedOuthop = pow ( 2.0 , 8.0 + 2.0 * log10 ( ratio ) ) ;
}
2022-06-14 15:01:44 +01:00
if ( proposedOuthop > 512.0 ) proposedOuthop = 512.0 ;
if ( proposedOuthop < 128.0 ) proposedOuthop = 128.0 ;
2022-06-22 09:10:02 +01:00
m_log . log ( 1 , " calculateHop: ratio and proposed outhop " , ratio , proposedOuthop ) ;
2022-06-14 15:01:44 +01:00
double inhop = proposedOuthop / ratio ;
if ( inhop < 1.0 ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " WARNING: Extreme ratio yields ideal inhop < 1, results may be suspect " , ratio , inhop ) ;
2022-06-14 15:01:44 +01:00
inhop = 1.0 ;
}
if ( inhop > 768.0 ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " WARNING: Extreme ratio yields ideal inhop > 768, results may be suspect " , ratio , inhop ) ;
2022-06-14 15:01:44 +01:00
inhop = 768.0 ;
2022-05-23 17:59:40 +01:00
}
2022-05-25 14:10:41 +01:00
2022-06-15 10:00:49 +01:00
m_inhop = int ( floor ( inhop ) ) ;
2022-05-26 15:08:07 +01:00
2022-06-22 09:10:02 +01:00
m_log . log ( 1 , " calculateHop: inhop and mean outhop " , m_inhop , m_inhop * ratio ) ;
2022-05-23 15:04:34 +01:00
}
2022-06-13 16:06:21 +01:00
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : updateRatioFromMap ( )
2022-06-13 16:06:21 +01:00
{
if ( m_keyFrameMap . empty ( ) ) return ;
2022-06-17 17:52:28 +01:00
if ( m_processInputDuration = = 0 ) {
m_timeRatio = double ( m_keyFrameMap . begin ( ) - > second ) /
double ( m_keyFrameMap . begin ( ) - > first ) ;
2022-06-21 17:03:24 +01:00
2022-06-22 09:10:02 +01:00
m_log . log ( 1 , " initial key-frame map entry " ,
2022-06-21 17:03:24 +01:00
double ( m_keyFrameMap . begin ( ) - > first ) ,
double ( m_keyFrameMap . begin ( ) - > second ) ) ;
2022-06-22 09:10:02 +01:00
m_log . log ( 1 , " giving initial ratio " , m_timeRatio ) ;
2022-06-21 17:03:24 +01:00
2022-06-17 17:52:28 +01:00
calculateHop ( ) ;
m_lastKeyFrameSurpassed = 0 ;
return ;
}
auto i0 = m_keyFrameMap . upper_bound ( m_lastKeyFrameSurpassed ) ;
if ( i0 = = m_keyFrameMap . end ( ) ) {
return ;
}
if ( m_processInputDuration > = i0 - > first ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 2 , " input duration surpasses pending key frame " ,
2022-06-21 17:03:24 +01:00
double ( m_processInputDuration ) , double ( i0 - > first ) ) ;
2022-06-17 17:52:28 +01:00
auto i1 = m_keyFrameMap . upper_bound ( m_processInputDuration ) ;
size_t keyFrameAtInput , keyFrameAtOutput ;
if ( i1 ! = m_keyFrameMap . end ( ) ) {
keyFrameAtInput = i1 - > first ;
keyFrameAtOutput = i1 - > second ;
} else {
keyFrameAtInput = m_studyInputDuration ;
keyFrameAtOutput = m_totalTargetDuration ;
}
// size_t toKeyFrameAtInput = keyFrameAtInput - i0->first;
// size_t toKeyFrameAtOutput = keyFrameAtOutput - i0->second;
size_t toKeyFrameAtInput = keyFrameAtInput - m_processInputDuration ;
size_t toKeyFrameAtOutput = 0 ;
if ( keyFrameAtOutput > m_totalOutputDuration ) {
toKeyFrameAtOutput = keyFrameAtOutput - m_totalOutputDuration ;
}
double ratio = double ( toKeyFrameAtOutput ) / double ( toKeyFrameAtInput ) ;
2022-06-22 09:10:02 +01:00
m_log . log ( 2 , " next key frame input and output " ,
2022-06-21 17:03:24 +01:00
double ( keyFrameAtInput ) , double ( keyFrameAtOutput ) ) ;
2022-06-22 09:10:02 +01:00
m_log . log ( 2 , " current input and output " ,
2022-06-21 17:03:24 +01:00
double ( m_processInputDuration ) , double ( m_totalOutputDuration ) ) ;
2022-06-22 09:10:02 +01:00
m_log . log ( 2 , " to next key frame input and output " ,
2022-06-21 17:03:24 +01:00
double ( toKeyFrameAtInput ) , double ( toKeyFrameAtOutput ) ) ;
2022-06-22 09:10:02 +01:00
m_log . log ( 2 , " new ratio " , ratio ) ;
2022-06-17 17:52:28 +01:00
m_timeRatio = ratio ;
calculateHop ( ) ;
m_lastKeyFrameSurpassed = i0 - > first ;
}
2022-06-13 16:06:21 +01:00
}
2022-05-23 15:04:34 +01:00
double
2022-06-21 10:25:08 +01:00
R3Stretcher : : getTimeRatio ( ) const
2022-05-23 15:04:34 +01:00
{
return m_timeRatio ;
}
double
2022-06-21 10:25:08 +01:00
R3Stretcher : : getPitchScale ( ) const
2022-05-23 15:04:34 +01:00
{
return m_pitchScale ;
}
2022-06-13 10:39:13 +01:00
double
2022-06-21 10:25:08 +01:00
R3Stretcher : : getFormantScale ( ) const
2022-06-13 10:39:13 +01:00
{
return m_formantScale ;
}
2022-05-23 15:04:34 +01:00
size_t
2022-06-21 10:25:08 +01:00
R3Stretcher : : getLatency ( ) const
2022-05-23 15:04:34 +01:00
{
2022-06-13 16:06:21 +01:00
if ( ! isRealTime ( ) ) {
2022-06-10 18:15:58 +01:00
return 0 ;
} else {
double factor = m_pitchScale * 0.5 ;
return size_t ( ceil ( m_guideConfiguration . longestFftSize * factor ) ) ;
}
2022-05-23 15:04:34 +01:00
}
size_t
2022-06-21 10:25:08 +01:00
R3Stretcher : : getChannelCount ( ) const
2022-05-23 15:04:34 +01:00
{
return m_parameters . channels ;
}
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : reset ( )
2022-05-23 15:04:34 +01:00
{
2022-05-27 11:17:20 +01:00
m_calculator - > reset ( ) ;
m_resampler - > reset ( ) ;
for ( auto & it : m_scaleData ) {
it . second - > guided . reset ( ) ;
}
for ( auto & cd : m_channelData ) {
cd - > reset ( ) ;
}
m_prevInhop = m_inhop ;
m_prevOuthop = int ( round ( m_inhop * getEffectiveRatio ( ) ) ) ;
2022-06-13 16:06:21 +01:00
m_studyInputDuration = 0 ;
m_totalTargetDuration = 0 ;
m_processInputDuration = 0 ;
2022-06-17 17:52:28 +01:00
m_lastKeyFrameSurpassed = 0 ;
2022-06-13 16:06:21 +01:00
m_totalOutputDuration = 0 ;
m_keyFrameMap . clear ( ) ;
m_mode = ProcessMode : : JustCreated ;
}
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : study ( const float * const * , size_t samples , bool )
2022-06-13 16:06:21 +01:00
{
if ( isRealTime ( ) ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R3Stretcher::study: Not meaningful in realtime mode " ) ;
2022-06-13 16:06:21 +01:00
return ;
}
if ( m_mode = = ProcessMode : : Processing | | m_mode = = ProcessMode : : Finished ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R3Stretcher::study: Cannot study after processing " ) ;
2022-06-13 16:06:21 +01:00
return ;
}
if ( m_mode = = ProcessMode : : JustCreated ) {
m_studyInputDuration = 0 ;
}
m_mode = ProcessMode : : Studying ;
m_studyInputDuration + = samples ;
2022-05-23 15:04:34 +01:00
}
size_t
2022-06-21 10:25:08 +01:00
R3Stretcher : : getSamplesRequired ( ) const
2022-05-23 15:04:34 +01:00
{
2022-05-26 15:08:07 +01:00
if ( available ( ) ! = 0 ) return 0 ;
2022-05-23 15:04:34 +01:00
int longest = m_guideConfiguration . longestFftSize ;
2022-06-14 15:15:55 +01:00
int rs = m_channelData [ 0 ] - > inbuf - > getReadSpace ( ) ;
2022-05-23 15:04:34 +01:00
if ( rs < longest ) {
return longest - rs ;
} else {
return 0 ;
}
}
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : process ( const float * const * input , size_t samples , bool final )
2022-05-23 15:04:34 +01:00
{
2022-06-13 16:06:21 +01:00
if ( m_mode = = ProcessMode : : Finished ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R3Stretcher::process: Cannot process again after final chunk " ) ;
2022-06-13 16:06:21 +01:00
return ;
}
if ( ! isRealTime ( ) & & ! m_keyFrameMap . empty ( ) ) {
if ( m_mode = = ProcessMode : : Studying ) {
m_totalTargetDuration =
2022-06-14 16:52:09 +01:00
size_t ( round ( m_studyInputDuration * getEffectiveRatio ( ) ) ) ;
2022-06-13 16:06:21 +01:00
}
2022-06-17 17:52:28 +01:00
updateRatioFromMap ( ) ;
2022-06-13 16:06:21 +01:00
}
2022-05-23 16:45:41 +01:00
if ( final ) {
2022-06-13 16:06:21 +01:00
// We don't distinguish between Finished and "draining, but
// haven't yet delivered all the samples" because the
// distinction is meaningless internally - it only affects
// whether available() finds any samples in the buffer
m_mode = ProcessMode : : Finished ;
} else {
m_mode = ProcessMode : : Processing ;
2022-05-23 16:45:41 +01:00
}
2022-05-23 15:04:34 +01:00
size_t ws = m_channelData [ 0 ] - > inbuf - > getWriteSpace ( ) ;
if ( samples > ws ) {
//!!! check this
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R3Stretcher::process: WARNING: Forced to increase input buffer size. Either setMaxProcessSize was not properly called or process is being called repeatedly without retrieve. " ) ;
2022-05-23 15:04:34 +01:00
size_t newSize = m_channelData [ 0 ] - > inbuf - > getSize ( ) - ws + samples ;
for ( int c = 0 ; c < m_parameters . channels ; + + c ) {
2022-05-26 17:42:45 +01:00
auto newBuf = m_channelData [ c ] - > inbuf - > resized ( newSize ) ;
m_channelData [ c ] - > inbuf = std : : unique_ptr < RingBuffer < float > > ( newBuf ) ;
2022-05-23 15:04:34 +01:00
}
}
for ( int c = 0 ; c < m_parameters . channels ; + + c ) {
m_channelData [ c ] - > inbuf - > write ( input [ c ] , samples ) ;
}
2022-06-13 16:06:21 +01:00
m_processInputDuration + = samples ;
2022-05-23 15:04:34 +01:00
consume ( ) ;
}
int
2022-06-21 10:25:08 +01:00
R3Stretcher : : available ( ) const
2022-05-23 15:04:34 +01:00
{
2022-05-23 16:45:41 +01:00
int av = int ( m_channelData [ 0 ] - > outbuf - > getReadSpace ( ) ) ;
2022-06-13 16:06:21 +01:00
if ( av = = 0 & & m_mode = = ProcessMode : : Finished ) {
return - 1 ;
} else {
return av ;
}
2022-05-23 15:04:34 +01:00
}
size_t
2022-06-21 10:25:08 +01:00
R3Stretcher : : retrieve ( float * const * output , size_t samples ) const
2022-05-23 15:04:34 +01:00
{
2022-06-14 15:15:55 +01:00
int got = samples ;
2022-05-23 15:04:34 +01:00
2022-06-14 15:15:55 +01:00
for ( int c = 0 ; c < m_parameters . channels ; + + c ) {
int gotHere = m_channelData [ c ] - > outbuf - > read ( output [ c ] , got ) ;
2022-05-23 15:04:34 +01:00
if ( gotHere < got ) {
if ( c > 0 ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 0 , " R3Stretcher::retrieve: WARNING: channel imbalance detected " ) ;
2022-05-23 15:04:34 +01:00
}
2022-06-14 15:15:55 +01:00
got = std : : min ( got , std : : max ( gotHere , 0 ) ) ;
2022-05-23 15:04:34 +01:00
}
}
return got ;
}
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : consume ( )
2022-05-23 15:04:34 +01:00
{
int longest = m_guideConfiguration . longestFftSize ;
2022-05-25 13:47:40 +01:00
int channels = m_parameters . channels ;
2022-05-23 17:59:40 +01:00
2022-06-13 17:16:03 +01:00
//!!! todo: wire debug level & logger throughout
2022-05-25 14:10:41 +01:00
// m_calculator->setDebugLevel(3);
2022-05-27 15:03:40 +01:00
int inhop = m_inhop ;
double effectivePitchRatio = 1.0 / m_pitchScale ;
if ( m_resampler ) {
effectivePitchRatio = m_resampler - > getEffectiveRatio ( effectivePitchRatio ) ;
}
2022-05-24 12:00:54 +01:00
2022-05-27 15:03:40 +01:00
int outhop = m_calculator - > calculateSingle ( m_timeRatio ,
effectivePitchRatio ,
2022-05-23 17:59:40 +01:00
1.f ,
2022-05-25 14:10:41 +01:00
inhop ,
2022-05-23 17:59:40 +01:00
longest ,
2022-06-13 17:16:03 +01:00
longest ,
true ) ;
2022-05-27 10:06:31 +01:00
// Now inhop is the distance by which the input stream will be
// advanced after our current frame has been read, and outhop is
// the distance by which the output will be advanced after it has
// been emitted; m_prevInhop and m_prevOuthop are the
// corresponding values the last time a frame was processed (*not*
// just the last time this function was called, since we can
// return without doing anything if the output buffer is full).
//
// Our phase adjustments need to be based on the distances we have
// advanced the input and output since the previous frame, not the
// distances we are about to advance them, so they use the m_prev
// values.
2022-06-21 17:03:24 +01:00
2022-05-26 15:08:07 +01:00
if ( inhop ! = m_prevInhop ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 2 , " change in inhop " , double ( m_prevInhop ) , double ( inhop ) ) ;
2022-05-26 15:08:07 +01:00
}
if ( outhop ! = m_prevOuthop ) {
2022-06-22 09:10:02 +01:00
m_log . log ( 2 , " change in outhop " , double ( m_prevOuthop ) , double ( outhop ) ) ;
2022-05-26 15:08:07 +01:00
}
2022-06-21 17:03:24 +01:00
2022-05-23 20:55:56 +01:00
while ( m_channelData . at ( 0 ) - > outbuf - > getWriteSpace ( ) > = outhop ) {
2022-05-25 11:26:16 +01:00
// NB our ChannelData, ScaleData, and ChannelScaleData maps
// contain shared_ptrs; whenever we retain one of them in a
// variable, we do so by reference to avoid copying the
// shared_ptr (as that is not realtime safe). Same goes for
// the map iterators
2022-05-23 20:55:56 +01:00
int readSpace = m_channelData . at ( 0 ) - > inbuf - > getReadSpace ( ) ;
if ( readSpace < longest ) {
2022-06-13 16:06:21 +01:00
if ( m_mode = = ProcessMode : : Finished ) {
2022-05-23 20:55:56 +01:00
if ( readSpace = = 0 ) {
break ;
}
} else {
break ;
}
2022-05-23 17:59:40 +01:00
}
2022-05-23 20:55:56 +01:00
2022-05-25 11:26:16 +01:00
// Analysis
2022-05-25 09:43:08 +01:00
2022-05-25 13:47:40 +01:00
for ( int c = 0 ; c < channels ; + + c ) {
2022-06-09 15:26:16 +01:00
analyseChannel ( c , inhop , m_prevInhop , m_prevOuthop ) ;
2022-05-23 15:04:34 +01:00
}
2022-05-25 09:43:08 +01:00
// Phase update. This is synchronised across all channels
2022-05-24 17:35:23 +01:00
for ( auto & it : m_channelData [ 0 ] - > scales ) {
2022-05-23 15:04:34 +01:00
int fftSize = it . first ;
2022-05-25 13:47:40 +01:00
for ( int c = 0 ; c < channels ; + + c ) {
2022-05-24 17:35:23 +01:00
auto & cd = m_channelData . at ( c ) ;
2022-05-25 11:26:16 +01:00
auto & scale = cd - > scales . at ( fftSize ) ;
m_channelAssembly . mag [ c ] = scale - > mag . data ( ) ;
m_channelAssembly . phase [ c ] = scale - > phase . data ( ) ;
2022-06-07 12:12:06 +01:00
m_channelAssembly . prevMag [ c ] = scale - > prevMag . data ( ) ;
2022-05-23 15:04:34 +01:00
m_channelAssembly . guidance [ c ] = & cd - > guidance ;
2022-05-25 11:26:16 +01:00
m_channelAssembly . outPhase [ c ] = scale - > advancedPhase . data ( ) ;
2022-05-23 15:04:34 +01:00
}
m_scaleData . at ( fftSize ) - > guided . advance
( m_channelAssembly . outPhase . data ( ) ,
m_channelAssembly . mag . data ( ) ,
m_channelAssembly . phase . data ( ) ,
2022-06-07 12:12:06 +01:00
m_channelAssembly . prevMag . data ( ) ,
2022-05-23 15:04:34 +01:00
m_guideConfiguration ,
m_channelAssembly . guidance . data ( ) ,
2022-05-27 10:06:31 +01:00
m_prevInhop ,
m_prevOuthop ) ;
2022-05-23 15:04:34 +01:00
}
2022-06-13 09:40:26 +01:00
for ( int c = 0 ; c < channels ; + + c ) {
adjustPreKick ( c ) ;
}
2022-05-25 11:26:16 +01:00
// Resynthesis
2022-05-25 09:43:08 +01:00
2022-05-25 13:47:40 +01:00
for ( int c = 0 ; c < channels ; + + c ) {
2022-05-25 11:26:16 +01:00
synthesiseChannel ( c , outhop ) ;
}
2022-05-25 13:47:40 +01:00
// Resample
int resampledCount = 0 ;
if ( m_resampler ) {
for ( int c = 0 ; c < channels ; + + c ) {
auto & cd = m_channelData . at ( c ) ;
m_channelAssembly . mixdown [ c ] = cd - > mixdown . data ( ) ;
m_channelAssembly . resampled [ c ] = cd - > resampled . data ( ) ;
}
resampledCount = m_resampler - > resample
( m_channelAssembly . resampled . data ( ) ,
m_channelData [ 0 ] - > resampled . size ( ) ,
m_channelAssembly . mixdown . data ( ) ,
outhop ,
1.0 / m_pitchScale ,
2022-06-13 16:06:21 +01:00
m_mode = = ProcessMode : : Finished & & readSpace < longest ) ;
2022-05-25 13:47:40 +01:00
}
// Emit
for ( int c = 0 ; c < channels ; + + c ) {
auto & cd = m_channelData . at ( c ) ;
if ( m_resampler ) {
cd - > outbuf - > write ( cd - > resampled . data ( ) , resampledCount ) ;
2022-06-17 17:52:28 +01:00
if ( c = = 0 ) m_totalOutputDuration + = resampledCount ;
2022-05-25 13:47:40 +01:00
} else {
cd - > outbuf - > write ( cd - > mixdown . data ( ) , outhop ) ;
2022-06-17 17:52:28 +01:00
if ( c = = 0 ) m_totalOutputDuration + = outhop ;
2022-05-25 13:47:40 +01:00
}
2022-06-10 18:15:58 +01:00
2022-05-25 13:47:40 +01:00
int readSpace = cd - > inbuf - > getReadSpace ( ) ;
2022-05-25 14:10:41 +01:00
if ( readSpace < inhop ) {
2022-06-13 16:06:21 +01:00
// This should happen only when draining (Finished)
2022-05-25 13:47:40 +01:00
cd - > inbuf - > skip ( readSpace ) ;
} else {
2022-05-25 14:10:41 +01:00
cd - > inbuf - > skip ( inhop ) ;
2022-05-25 13:47:40 +01:00
}
}
2022-06-10 20:26:37 +01:00
if ( m_startSkip > 0 ) {
int toSkip = std : : min
( m_startSkip , m_channelData . at ( 0 ) - > outbuf - > getReadSpace ( ) ) ;
for ( int c = 0 ; c < channels ; + + c ) {
m_channelData . at ( c ) - > outbuf - > skip ( toSkip ) ;
}
m_startSkip - = toSkip ;
}
2022-06-10 18:15:58 +01:00
2022-05-27 10:06:31 +01:00
m_prevInhop = inhop ;
m_prevOuthop = outhop ;
}
2022-05-25 11:26:16 +01:00
}
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : analyseChannel ( int c , int inhop , int prevInhop , int prevOuthop )
2022-05-25 11:26:16 +01:00
{
int longest = m_guideConfiguration . longestFftSize ;
int classify = m_guideConfiguration . classificationFftSize ;
auto & cd = m_channelData . at ( c ) ;
double * buf = cd - > scales . at ( longest ) - > timeDomain . data ( ) ;
int readSpace = cd - > inbuf - > getReadSpace ( ) ;
if ( readSpace < longest ) {
cd - > inbuf - > peek ( buf , readSpace ) ;
v_zero ( buf + readSpace , longest - readSpace ) ;
} else {
cd - > inbuf - > peek ( buf , longest ) ;
}
2022-05-25 16:25:03 +01:00
// We have a single unwindowed frame at the longest FFT size
// ("scale"). Populate the shorter FFT sizes from the centre of
// it, windowing as we copy. The classification scale is handled
// separately because it has readahead, so skip it here as well as
// the longest. (In practice this means we are probably only
// populating one scale)
2022-05-25 11:26:16 +01:00
for ( auto & it : cd - > scales ) {
int fftSize = it . first ;
if ( fftSize = = classify | | fftSize = = longest ) continue ;
int offset = ( longest - fftSize ) / 2 ;
m_scaleData . at ( fftSize ) - > analysisWindow . cut
( buf + offset , it . second - > timeDomain . data ( ) ) ;
}
2022-05-25 14:10:41 +01:00
// The classification scale has a one-hop readahead, so populate
2022-05-26 15:08:07 +01:00
// the readahead from further down the long unwindowed frame.
2022-05-23 17:36:26 +01:00
2022-05-25 11:26:16 +01:00
auto & classifyScale = cd - > scales . at ( classify ) ;
ClassificationReadaheadData & readahead = cd - > readahead ;
2022-05-23 17:36:26 +01:00
2022-05-25 11:26:16 +01:00
m_scaleData . at ( classify ) - > analysisWindow . cut
2022-05-25 14:10:41 +01:00
( buf + ( longest - classify ) / 2 + inhop ,
2022-05-25 11:26:16 +01:00
readahead . timeDomain . data ( ) ) ;
2022-05-26 15:08:07 +01:00
// If inhop has changed since the previous frame, we'll have to
// populate the classification scale (but for analysis/resynthesis
// rather than classification) anew rather than reuse the previous
// readahead. Pity...
2022-05-27 11:17:20 +01:00
bool haveValidReadahead = cd - > haveReadahead ;
if ( inhop ! = prevInhop ) haveValidReadahead = false ;
if ( ! haveValidReadahead ) {
2022-05-26 15:08:07 +01:00
m_scaleData . at ( classify ) - > analysisWindow . cut
( buf + ( longest - classify ) / 2 ,
classifyScale - > timeDomain . data ( ) ) ;
}
2022-05-25 11:26:16 +01:00
// Finally window the longest scale
m_scaleData . at ( longest ) - > analysisWindow . cut ( buf ) ;
// FFT shift, forward FFT, and carry out cartesian-polar
// conversion for each FFT size.
2022-05-26 15:08:07 +01:00
// For the classification scale we need magnitudes for the full
// range (polar only in a subset) and we operate in the readahead,
// pulling current values from the existing readahead (except
// where the inhop has changed as above, in which case we need to
// do both readahead and current)
2022-05-25 11:26:16 +01:00
2022-05-27 11:17:20 +01:00
if ( haveValidReadahead ) {
2022-05-26 15:08:07 +01:00
v_copy ( classifyScale - > mag . data ( ) ,
readahead . mag . data ( ) ,
classifyScale - > bufSize ) ;
v_copy ( classifyScale - > phase . data ( ) ,
readahead . phase . data ( ) ,
classifyScale - > bufSize ) ;
}
2022-05-25 11:26:16 +01:00
2022-06-09 16:39:30 +01:00
v_fftshift ( readahead . timeDomain . data ( ) , classify ) ;
2022-05-25 11:26:16 +01:00
m_scaleData . at ( classify ) - > fft . forward ( readahead . timeDomain . data ( ) ,
classifyScale - > real . data ( ) ,
classifyScale - > imag . data ( ) ) ;
for ( const auto & b : m_guideConfiguration . fftBandLimits ) {
if ( b . fftSize = = classify ) {
2022-06-17 16:32:14 +01:00
ToPolarSpec spec ;
spec . magFromBin = 0 ;
spec . magBinCount = classify / 2 + 1 ;
spec . polarFromBin = b . b0min ;
spec . polarBinCount = b . b1max - b . b0min + 1 ;
convertToPolar ( readahead . mag . data ( ) ,
readahead . phase . data ( ) ,
classifyScale - > real . data ( ) ,
classifyScale - > imag . data ( ) ,
spec ) ;
2022-05-25 11:26:16 +01:00
v_scale ( classifyScale - > mag . data ( ) ,
1.0 / double ( classify ) ,
classifyScale - > mag . size ( ) ) ;
break ;
}
}
2022-05-27 11:17:20 +01:00
cd - > haveReadahead = true ;
2022-05-27 14:58:42 +01:00
// For the others (and the classify as well, if the inhop has
// changed or we haven't filled the readahead yet) we operate
// directly in the scale data and restrict the range for
2022-05-26 15:08:07 +01:00
// cartesian-polar conversion
2022-05-25 11:26:16 +01:00
for ( auto & it : cd - > scales ) {
int fftSize = it . first ;
2022-06-06 17:44:22 +01:00
if ( fftSize = = classify & & haveValidReadahead ) {
continue ;
}
2022-05-25 11:26:16 +01:00
auto & scale = it . second ;
v_fftshift ( scale - > timeDomain . data ( ) , fftSize ) ;
m_scaleData . at ( fftSize ) - > fft . forward ( scale - > timeDomain . data ( ) ,
scale - > real . data ( ) ,
scale - > imag . data ( ) ) ;
for ( const auto & b : m_guideConfiguration . fftBandLimits ) {
if ( b . fftSize = = fftSize ) {
2022-06-17 16:32:14 +01:00
ToPolarSpec spec ;
// For the classify scale we always want the full
2022-06-17 16:56:09 +01:00
// range, as all the magnitudes (though not
// necessarily all phases) are potentially relevant to
// classification and formant analysis. But this case
// here only happens if we don't haveValidReadahead -
// the normal case is above and just copies from the
// previous readahead.
2022-06-17 16:32:14 +01:00
if ( fftSize = = classify ) {
spec . magFromBin = 0 ;
spec . magBinCount = classify / 2 + 1 ;
spec . polarFromBin = b . b0min ;
spec . polarBinCount = b . b1max - b . b0min + 1 ;
} else {
spec . magFromBin = b . b0min ;
spec . magBinCount = b . b1max - b . b0min + 1 ;
spec . polarFromBin = spec . magFromBin ;
spec . polarBinCount = spec . magBinCount ;
}
convertToPolar ( scale - > mag . data ( ) ,
scale - > phase . data ( ) ,
scale - > real . data ( ) ,
scale - > imag . data ( ) ,
spec ) ;
v_scale ( scale - > mag . data ( ) + spec . magFromBin ,
2022-05-25 11:26:16 +01:00
1.0 / double ( fftSize ) ,
2022-06-17 16:32:14 +01:00
spec . magBinCount ) ;
2022-05-25 11:26:16 +01:00
break ;
2022-05-23 15:04:34 +01:00
}
2022-05-25 11:26:16 +01:00
}
}
2022-06-06 16:51:02 +01:00
if ( m_parameters . options & RubberBandStretcher : : OptionFormantPreserved ) {
analyseFormant ( c ) ;
adjustFormant ( c ) ;
}
2022-06-06 16:37:44 +01:00
2022-05-25 16:25:03 +01:00
// Use the classification scale to get a bin segmentation and
// calculate the adaptive frequency guide for this channel
2022-05-27 14:58:42 +01:00
v_copy ( cd - > classification . data ( ) , cd - > nextClassification . data ( ) ,
cd - > classification . size ( ) ) ;
cd - > classifier - > classify ( readahead . mag . data ( ) ,
cd - > nextClassification . data ( ) ) ;
2022-05-25 11:26:16 +01:00
cd - > prevSegmentation = cd - > segmentation ;
cd - > segmentation = cd - > nextSegmentation ;
2022-05-27 14:58:42 +01:00
cd - > nextSegmentation = cd - > segmenter - > segment ( cd - > nextClassification . data ( ) ) ;
2022-06-09 16:00:27 +01:00
/*
2022-06-09 14:16:49 +01:00
if ( c = = 0 ) {
double pb = cd - > nextSegmentation . percussiveBelow ;
double pa = cd - > nextSegmentation . percussiveAbove ;
double ra = cd - > nextSegmentation . residualAbove ;
2022-06-13 10:08:05 +01:00
int pbb = binForFrequency ( pb , classify , m_parameters . sampleRate ) ;
int pab = binForFrequency ( pa , classify , m_parameters . sampleRate ) ;
int rab = binForFrequency ( ra , classify , m_parameters . sampleRate ) ;
2022-06-09 14:16:49 +01:00
std : : cout < < " pb = " < < pb < < " , pbb = " < < pbb < < std : : endl ;
std : : cout < < " pa = " < < pa < < " , pab = " < < pab < < std : : endl ;
std : : cout < < " ra = " < < ra < < " , rab = " < < rab < < std : : endl ;
std : : cout < < " s: " ;
for ( int i = 0 ; i < = classify / 2 ; + + i ) {
if ( i > 0 ) std : : cout < < " , " ;
if ( i < pbb | | ( i > = pab & & i < = rab ) ) {
std : : cout < < " 1 " ;
} else {
std : : cout < < " 0 " ;
}
}
std : : cout < < std : : endl ;
}
2022-06-13 09:40:26 +01:00
*/
2022-06-17 15:01:26 +01:00
double ratio = getEffectiveRatio ( ) ;
if ( fabs ( ratio - 1.0 ) < 1.0e-6 ) {
+ + m_unityCount ;
} else {
m_unityCount = 0 ;
}
2022-05-27 14:58:42 +01:00
2022-06-17 15:01:26 +01:00
m_guide . updateGuidance ( ratio ,
2022-06-14 15:01:44 +01:00
prevOuthop ,
2022-06-09 14:29:51 +01:00
classifyScale - > mag . data ( ) ,
classifyScale - > prevMag . data ( ) ,
2022-06-13 09:40:26 +01:00
cd - > readahead . mag . data ( ) ,
2022-06-09 14:29:51 +01:00
cd - > segmentation ,
cd - > prevSegmentation ,
cd - > nextSegmentation ,
2022-06-17 15:01:26 +01:00
v_mean ( classifyScale - > mag . data ( ) + 1 , classify / 2 ) ,
m_unityCount ,
2022-06-09 14:29:51 +01:00
cd - > guidance ) ;
2022-06-13 09:40:26 +01:00
/*
if ( c = = 0 ) {
if ( cd - > guidance . kick . present ) {
std : : cout < < " k:2 " < < std : : endl ;
} else if ( cd - > guidance . preKick . present ) {
std : : cout < < " k:1 " < < std : : endl ;
} else {
std : : cout < < " k:0 " < < std : : endl ;
}
}
*/
2022-05-25 11:26:16 +01:00
}
2022-05-27 14:58:42 +01:00
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : analyseFormant ( int c )
2022-05-27 14:58:42 +01:00
{
2022-06-06 16:51:02 +01:00
auto & cd = m_channelData . at ( c ) ;
auto & f = * cd - > formant ;
2022-05-27 14:58:42 +01:00
2022-06-06 16:51:02 +01:00
int fftSize = f . fftSize ;
int binCount = fftSize / 2 + 1 ;
2022-05-27 14:58:42 +01:00
2022-06-06 16:51:02 +01:00
auto & scale = cd - > scales . at ( fftSize ) ;
auto & scaleData = m_scaleData . at ( fftSize ) ;
2022-05-27 14:58:42 +01:00
2022-06-06 16:51:02 +01:00
scaleData - > fft . inverseCepstral ( scale - > mag . data ( ) , f . cepstra . data ( ) ) ;
2022-05-27 14:58:42 +01:00
2022-06-06 14:00:09 +01:00
int cutoff = int ( floor ( m_parameters . sampleRate / 650.0 ) ) ;
2022-05-27 14:58:42 +01:00
if ( cutoff < 1 ) cutoff = 1 ;
f . cepstra [ 0 ] / = 2.0 ;
f . cepstra [ cutoff - 1 ] / = 2.0 ;
2022-06-06 16:51:02 +01:00
for ( int i = cutoff ; i < fftSize ; + + i ) {
2022-05-27 14:58:42 +01:00
f . cepstra [ i ] = 0.0 ;
}
2022-06-06 16:51:02 +01:00
v_scale ( f . cepstra . data ( ) , 1.0 / double ( fftSize ) , cutoff ) ;
2022-05-27 14:58:42 +01:00
2022-06-06 16:51:02 +01:00
scaleData - > fft . forward ( f . cepstra . data ( ) , f . envelope . data ( ) , f . spare . data ( ) ) ;
2022-05-27 14:58:42 +01:00
v_exp ( f . envelope . data ( ) , binCount ) ;
2022-06-06 13:09:29 +01:00
v_square ( f . envelope . data ( ) , binCount ) ;
2022-05-27 14:58:42 +01:00
for ( int i = 0 ; i < binCount ; + + i ) {
if ( f . envelope [ i ] > 1.0e10 ) f . envelope [ i ] = 1.0e10 ;
}
}
2022-06-06 16:37:44 +01:00
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : adjustFormant ( int c )
2022-06-06 16:37:44 +01:00
{
auto & cd = m_channelData . at ( c ) ;
for ( auto & it : cd - > scales ) {
int fftSize = it . first ;
auto & scale = it . second ;
2022-06-06 17:10:12 +01:00
int highBin = int ( floor ( fftSize * 10000.0 / m_parameters . sampleRate ) ) ;
2022-06-06 16:51:02 +01:00
double targetFactor = double ( cd - > formant - > fftSize ) / double ( fftSize ) ;
2022-06-13 10:39:13 +01:00
double formantScale = m_formantScale ;
if ( formantScale = = 0.0 ) formantScale = 1.0 / m_pitchScale ;
double sourceFactor = targetFactor / formantScale ;
2022-06-06 17:10:12 +01:00
double maxRatio = 60.0 ;
double minRatio = 1.0 / maxRatio ;
2022-06-06 17:06:52 +01:00
for ( const auto & b : m_guideConfiguration . fftBandLimits ) {
2022-06-06 17:10:12 +01:00
if ( b . fftSize ! = fftSize ) continue ;
for ( int i = b . b0min ; i < b . b1max & & i < highBin ; + + i ) {
double source = cd - > formant - > envelopeAt ( i * sourceFactor ) ;
double target = cd - > formant - > envelopeAt ( i * targetFactor ) ;
if ( target > 0.0 ) {
double ratio = source / target ;
if ( ratio < minRatio ) ratio = minRatio ;
if ( ratio > maxRatio ) ratio = maxRatio ;
scale - > mag [ i ] * = ratio ;
2022-06-06 17:06:52 +01:00
}
2022-06-06 16:37:44 +01:00
}
}
}
}
2022-06-13 09:40:26 +01:00
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : adjustPreKick ( int c )
2022-06-13 09:40:26 +01:00
{
auto & cd = m_channelData . at ( c ) ;
auto fftSize = cd - > guidance . fftBands [ 0 ] . fftSize ;
if ( cd - > guidance . preKick . present ) {
auto & scale = cd - > scales . at ( fftSize ) ;
2022-06-13 10:08:05 +01:00
int from = binForFrequency ( cd - > guidance . preKick . f0 ,
fftSize , m_parameters . sampleRate ) ;
int to = binForFrequency ( cd - > guidance . preKick . f1 ,
fftSize , m_parameters . sampleRate ) ;
2022-06-13 09:40:26 +01:00
for ( int i = from ; i < = to ; + + i ) {
double diff = scale - > mag [ i ] - scale - > prevMag [ i ] ;
if ( diff > 0.0 ) {
scale - > pendingKick [ i ] = diff ;
scale - > mag [ i ] - = diff ;
}
}
} else if ( cd - > guidance . kick . present ) {
auto & scale = cd - > scales . at ( fftSize ) ;
2022-06-13 10:08:05 +01:00
int from = binForFrequency ( cd - > guidance . preKick . f0 ,
fftSize , m_parameters . sampleRate ) ;
int to = binForFrequency ( cd - > guidance . preKick . f1 ,
fftSize , m_parameters . sampleRate ) ;
2022-06-13 09:40:26 +01:00
for ( int i = from ; i < = to ; + + i ) {
scale - > mag [ i ] + = scale - > pendingKick [ i ] ;
scale - > pendingKick [ i ] = 0.0 ;
}
}
}
2022-05-25 11:26:16 +01:00
void
2022-06-21 10:25:08 +01:00
R3Stretcher : : synthesiseChannel ( int c , int outhop )
2022-05-25 11:26:16 +01:00
{
int longest = m_guideConfiguration . longestFftSize ;
auto & cd = m_channelData . at ( c ) ;
2022-05-23 15:04:34 +01:00
2022-05-25 11:26:16 +01:00
for ( const auto & band : cd - > guidance . fftBands ) {
int fftSize = band . fftSize ;
auto & scale = cd - > scales . at ( fftSize ) ;
auto & scaleData = m_scaleData . at ( fftSize ) ;
2022-06-17 16:56:09 +01:00
// copy to prevMag before filtering
v_copy ( scale - > prevMag . data ( ) ,
scale - > mag . data ( ) ,
scale - > bufSize ) ;
2022-06-09 16:39:30 +01:00
double winscale = double ( outhop ) / scaleData - > windowScaleFactor ;
2022-05-25 09:43:08 +01:00
2022-05-25 16:25:03 +01:00
// The frequency filter is applied naively in the frequency
// domain. Aliasing is reduced by the shorter resynthesis
2022-06-17 16:52:55 +01:00
// window. We resynthesise each scale individually, then sum -
// it's easier to manage scaling for in situations with a
// varying resynthesis hop
2022-06-13 10:08:05 +01:00
int lowBin = binForFrequency ( band . f0 , fftSize , m_parameters . sampleRate ) ;
int highBin = binForFrequency ( band . f1 , fftSize , m_parameters . sampleRate ) ;
2022-06-06 17:50:22 +01:00
if ( highBin % 2 = = 0 & & highBin > 0 ) - - highBin ;
2022-06-06 17:10:12 +01:00
2022-06-17 16:52:55 +01:00
if ( lowBin > 0 ) {
v_zero ( scale - > real . data ( ) , lowBin ) ;
v_zero ( scale - > imag . data ( ) , lowBin ) ;
2022-06-06 12:08:52 +01:00
}
2022-05-25 09:43:08 +01:00
2022-06-17 16:52:55 +01:00
v_scale ( scale - > mag . data ( ) + lowBin , winscale , highBin - lowBin ) ;
v_polar_to_cartesian ( scale - > real . data ( ) + lowBin ,
scale - > imag . data ( ) + lowBin ,
scale - > mag . data ( ) + lowBin ,
scale - > advancedPhase . data ( ) + lowBin ,
highBin - lowBin ) ;
2022-06-17 16:56:09 +01:00
if ( highBin < scale - > bufSize ) {
v_zero ( scale - > real . data ( ) + highBin , scale - > bufSize - highBin ) ;
v_zero ( scale - > imag . data ( ) + highBin , scale - > bufSize - highBin ) ;
2022-05-25 11:26:16 +01:00
}
2022-05-24 16:54:05 +01:00
2022-05-25 11:26:16 +01:00
scaleData - > fft . inverse ( scale - > real . data ( ) ,
scale - > imag . data ( ) ,
scale - > timeDomain . data ( ) ) ;
2022-05-24 16:54:05 +01:00
2022-05-25 11:26:16 +01:00
v_fftshift ( scale - > timeDomain . data ( ) , fftSize ) ;
2022-05-24 16:54:05 +01:00
2022-05-25 16:25:03 +01:00
// Synthesis window may be shorter than analysis window, so
// copy and cut only from the middle of the time-domain frame;
// and the accumulator length always matches the longest FFT
// size, so as to make mixing straightforward, so there is an
// additional offset needed for the target
2022-05-25 09:43:08 +01:00
2022-05-25 11:26:16 +01:00
int synthesisWindowSize = scaleData - > synthesisWindow . getSize ( ) ;
int fromOffset = ( fftSize - synthesisWindowSize ) / 2 ;
int toOffset = ( longest - synthesisWindowSize ) / 2 ;
scaleData - > synthesisWindow . cutAndAdd
( scale - > timeDomain . data ( ) + fromOffset ,
scale - > accumulator . data ( ) + toOffset ) ;
}
2022-05-25 09:43:08 +01:00
2022-05-25 13:47:40 +01:00
// Mix this channel and move the accumulator along
2022-05-24 12:00:54 +01:00
2022-05-25 13:47:40 +01:00
float * mixptr = cd - > mixdown . data ( ) ;
2022-05-25 11:26:16 +01:00
v_zero ( mixptr , outhop ) ;
2022-05-23 15:04:34 +01:00
2022-05-25 11:26:16 +01:00
for ( auto & it : cd - > scales ) {
auto & scale = it . second ;
2022-05-23 20:55:56 +01:00
2022-05-25 11:26:16 +01:00
double * accptr = scale - > accumulator . data ( ) ;
2022-05-25 13:47:40 +01:00
for ( int i = 0 ; i < outhop ; + + i ) {
mixptr [ i ] + = float ( accptr [ i ] ) ;
}
2022-05-24 12:00:54 +01:00
2022-05-25 11:26:16 +01:00
int n = scale - > accumulator . size ( ) - outhop ;
v_move ( accptr , accptr + outhop , n ) ;
v_zero ( accptr + n , outhop ) ;
}
2022-05-23 15:04:34 +01:00
}
}