mirror of
https://github.com/RPCS3/soundtouch.git
synced 2024-11-10 04:42:50 +01:00
- Redesigned quickseek algorithm for improved sound quality in quickseek mode
- Adaptive integer divider scaling for improved sound quality when using integer processing - Version 1.9.1-pre
This commit is contained in:
parent
c9507ff7f1
commit
db04025351
76
README.html
76
README.html
@ -9,11 +9,11 @@
|
|||||||
<meta name="description"
|
<meta name="description"
|
||||||
content="Readme file for SoundTouch audio processing library">
|
content="Readme file for SoundTouch audio processing library">
|
||||||
<style> <!-- .normal { font-family: Arial }
|
<style> <!-- .normal { font-family: Arial }
|
||||||
--></style>
|
--></style>
|
||||||
</head>
|
</head>
|
||||||
<body class="normal">
|
<body class="normal">
|
||||||
<hr>
|
<hr>
|
||||||
<h1>SoundTouch audio processing library v1.9</h1>
|
<h1>SoundTouch audio processing library v1.9.1-pre</h1>
|
||||||
<p class="normal">SoundTouch library Copyright © Olli Parviainen 2001-2015</p>
|
<p class="normal">SoundTouch library Copyright © Olli Parviainen 2001-2015</p>
|
||||||
<hr>
|
<hr>
|
||||||
<h2>1. Introduction </h2>
|
<h2>1. Introduction </h2>
|
||||||
@ -60,10 +60,10 @@ the compilation, the target program will require additional vcomp dll library to
|
|||||||
properly run. In Visual C++ 9.0 these libraries can be found in the following
|
properly run. In Visual C++ 9.0 these libraries can be found in the following
|
||||||
folders.</p>
|
folders.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>x86 32bit: C:\Program Files (x86)\Microsoft Visual Studio
|
<li>x86 32bit: C:\Program Files (x86)\Microsoft Visual Studio
|
||||||
9.0\VC\redist\x86\Microsoft.VC90.OPENMP\vcomp90.dll</li>
|
9.0\VC\redist\x86\Microsoft.VC90.OPENMP\vcomp90.dll</li>
|
||||||
<li>x64 64bit: C:\Program Files (x86)\Microsoft Visual Studio
|
<li>x64 64bit: C:\Program Files (x86)\Microsoft Visual Studio
|
||||||
9.0\VC\redist\amd64\Microsoft.VC90.OPENMP\vcomp90.dll</li>
|
9.0\VC\redist\amd64\Microsoft.VC90.OPENMP\vcomp90.dll</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>In Visual Studio 2008, a SP1 version may be required for these libraries. In
|
<p>In Visual Studio 2008, a SP1 version may be required for these libraries. In
|
||||||
other VC++ versions the required library will be expectedly found in similar
|
other VC++ versions the required library will be expectedly found in similar
|
||||||
@ -103,8 +103,8 @@ Notice that "configure" file is not available before running the
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<p>Builds the SoundTouch library & SoundStretch utility. You can
|
<p>Builds the SoundTouch library & SoundStretch utility. You can
|
||||||
optionally add "-j" switch after "make" to speed up the compilation in
|
optionally add "-j" switch after "make" to speed up the compilation in
|
||||||
multi-core systems.</p>
|
multi-core systems.</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr valign="top">
|
<tr valign="top">
|
||||||
@ -355,8 +355,8 @@ computation burden</td>
|
|||||||
<h3>3.5 Performance Optimizations </h3>
|
<h3>3.5 Performance Optimizations </h3>
|
||||||
<p><strong>General optimizations:</strong></p>
|
<p><strong>General optimizations:</strong></p>
|
||||||
<p>The time-stretch routine has a 'quick' mode that substantially
|
<p>The time-stretch routine has a 'quick' mode that substantially
|
||||||
speeds up the algorithm but may degrade the sound quality by a small
|
speeds up the algorithm but may slightly compromise the sound quality.
|
||||||
amount. This mode is activated by calling SoundTouch::setSetting()
|
This mode is activated by calling SoundTouch::setSetting()
|
||||||
function with parameter id of SETTING_USE_QUICKSEEK and value
|
function with parameter id of SETTING_USE_QUICKSEEK and value
|
||||||
"1", i.e. </p>
|
"1", i.e. </p>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
@ -368,7 +368,7 @@ intrinsics, providing about a 3x processing speedup for x86 compatible
|
|||||||
processors vs. non-SIMD implementation:</p>
|
processors vs. non-SIMD implementation:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li> Intel MMX optimized routines are used with x86 CPUs when 16bit integer
|
<li> Intel MMX optimized routines are used with x86 CPUs when 16bit integer
|
||||||
sample type is used</li>
|
sample type is used</li>
|
||||||
<li> Intel SSE optimized routines are used with x86 CPUs when 32bit floating
|
<li> Intel SSE optimized routines are used with x86 CPUs when 32bit floating
|
||||||
point sample type is used</li>
|
point sample type is used</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -395,17 +395,17 @@ This include for example multi-core embedded devices.</p>
|
|||||||
<p>OpenMP parallel computation can be enabled before compiling SoundTouch
|
<p>OpenMP parallel computation can be enabled before compiling SoundTouch
|
||||||
library as follows:</p>
|
library as follows:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Visual Studio</strong>: Open properties for the <strong>SoundTouch
|
<li><strong>Visual Studio</strong>: Open properties for the <strong>SoundTouch
|
||||||
</strong>sub-project, browse to <strong>C/C++</strong> and <strong>Language
|
</strong>sub-project, browse to <strong>C/C++</strong> and <strong>Language
|
||||||
</strong>settings. Set
|
</strong>settings. Set
|
||||||
there "<strong>OpenMP support</strong>" to "<strong>Yes</strong>". Alternatively add
|
there "<strong>OpenMP support</strong>" to "<strong>Yes</strong>". Alternatively add
|
||||||
<strong>/openmp</strong> switch to command-line
|
<strong>/openmp</strong> switch to command-line
|
||||||
parameters</li>
|
parameters</li>
|
||||||
<li><strong>GNU</strong>: Run the configure script with "<strong>./configure
|
<li><strong>GNU</strong>: Run the configure script with "<strong>./configure
|
||||||
--enable-openmp</strong>" switch, then run make as usually</li>
|
--enable-openmp</strong>" switch, then run make as usually</li>
|
||||||
<li><strong>Android</strong>: Add "<strong>-fopenmp</strong>" switches to compiler & linker
|
<li><strong>Android</strong>: Add "<strong>-fopenmp</strong>" switches to compiler & linker
|
||||||
options, see README-SoundTouch-Android.html in the source code package for
|
options, see README-SoundTouch-Android.html in the source code package for
|
||||||
more detailed instructions.</li>
|
more detailed instructions.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr>
|
<hr>
|
||||||
<h2><a name="SoundStretch"></a>4. SoundStretch audio processing utility
|
<h2><a name="SoundStretch"></a>4. SoundStretch audio processing utility
|
||||||
@ -566,18 +566,25 @@ this corresponds to lowering the pitch by -0.318 semitones:</p>
|
|||||||
<hr>
|
<hr>
|
||||||
<h2>5. Change History</h2>
|
<h2>5. Change History</h2>
|
||||||
<h3>5.1. SoundTouch library Change History </h3>
|
<h3>5.1. SoundTouch library Change History </h3>
|
||||||
|
<p><b>1.9.1-pre:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Improved SoundTouch::flush() function so that it returns precisely the desired amount of samples for exact output duration control</li>
|
||||||
|
<li>Redesigned quickseek algorithm for improved sound quality when using the quickseek mode. The new quickseek algorithm can find 99% as good results as the default full-scan mode.</li>
|
||||||
|
<li>Added adaptive integer divider scaling for improved sound quality when using integer processing algorithm
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<p><b>1.9:</b></p>
|
<p><b>1.9:</b></p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Added support for parallel computation support via OpenMP primitives for better performance in multicore systems.
|
<li>Added support for parallel computation support via OpenMP primitives for better performance in multicore systems.
|
||||||
Benchmarks show that achieved parallel processing speedup improvement
|
Benchmarks show that achieved parallel processing speedup improvement
|
||||||
typically range from +30% (x86 dual-core) to +180% (ARM quad-core). The
|
typically range from +30% (x86 dual-core) to +180% (ARM quad-core). The
|
||||||
OpenMP optimizations are disabled by default, see OpenMP notes above in this
|
OpenMP optimizations are disabled by default, see OpenMP notes above in this
|
||||||
readme file how to enabled these optimizations.</li>
|
readme file how to enabled these optimizations.</li>
|
||||||
<li>Android: Added support for Android devices featuring X86 and MIPS CPUs,
|
<li>Android: Added support for Android devices featuring X86 and MIPS CPUs,
|
||||||
in addition to ARM CPUs.</li>
|
in addition to ARM CPUs.</li>
|
||||||
<li>Android: More versatile Android example application that processes WAV
|
<li>Android: More versatile Android example application that processes WAV
|
||||||
audio files with SoundTouch library</li>
|
audio files with SoundTouch library</li>
|
||||||
<li>Replaced Windows-like 'BOOL' types with native 'bool'</li>
|
<li>Replaced Windows-like 'BOOL' types with native 'bool'</li>
|
||||||
<li>Changed documentation token to "dist_doc_DATA" in Makefile.am file</li>
|
<li>Changed documentation token to "dist_doc_DATA" in Makefile.am file</li>
|
||||||
<li>Miscellaneous small fixes and improvements</li>
|
<li>Miscellaneous small fixes and improvements</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -816,7 +823,7 @@ submitted bugfixes:</p>
|
|||||||
<li> David Clark</li>
|
<li> David Clark</li>
|
||||||
<li> Patrick Colis</li>
|
<li> Patrick Colis</li>
|
||||||
<li> Miquel Colon</li>
|
<li> Miquel Colon</li>
|
||||||
<li> Jim Credland</li>
|
<li> Jim Credland</li>
|
||||||
<li> Sandro Cumerlato</li>
|
<li> Sandro Cumerlato</li>
|
||||||
<li> Justin Frankel</li>
|
<li> Justin Frankel</li>
|
||||||
<li> Masa H.</li>
|
<li> Masa H.</li>
|
||||||
@ -827,10 +834,10 @@ submitted bugfixes:</p>
|
|||||||
<li> Yuval Naveh</li>
|
<li> Yuval Naveh</li>
|
||||||
<li> Paulo Pizarro</li>
|
<li> Paulo Pizarro</li>
|
||||||
<li> Blaise Potard</li>
|
<li> Blaise Potard</li>
|
||||||
<li> Michael Pruett</li>
|
<li> Michael Pruett</li>
|
||||||
<li> Rajeev Puran</li>
|
<li> Rajeev Puran</li>
|
||||||
<li> RJ Ryan</li>
|
<li> RJ Ryan</li>
|
||||||
<li> John Sheehy</li>
|
<li> John Sheehy</li>
|
||||||
<li> Tim Shuttleworth</li>
|
<li> Tim Shuttleworth</li>
|
||||||
<li> Albert Sirvent</li>
|
<li> Albert Sirvent</li>
|
||||||
<li> John Stumpo</li>
|
<li> John Stumpo</li>
|
||||||
@ -852,7 +859,8 @@ General Public License for more details.</p>
|
|||||||
License along with this library; if not, write to the Free Software
|
License along with this library; if not, write to the Free Software
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</p>
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</p>
|
||||||
<hr><!--
|
<hr><!--
|
||||||
$Id$
-->
|
$Id$
|
||||||
|
-->
|
||||||
<p>
|
<p>
|
||||||
<i>README.html file updated in May-2015</i></p>
|
<i>README.html file updated in May-2015</i></p>
|
||||||
</body>
|
</body>
|
||||||
|
@ -79,10 +79,10 @@ namespace soundtouch
|
|||||||
{
|
{
|
||||||
|
|
||||||
/// Soundtouch library version string
|
/// Soundtouch library version string
|
||||||
#define SOUNDTOUCH_VERSION "1.9.0"
|
#define SOUNDTOUCH_VERSION "1.9.1-pre"
|
||||||
|
|
||||||
/// SoundTouch library version id
|
/// SoundTouch library version id
|
||||||
#define SOUNDTOUCH_VERSION_ID (10900)
|
#define SOUNDTOUCH_VERSION_ID (10901)
|
||||||
|
|
||||||
//
|
//
|
||||||
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
||||||
@ -154,20 +154,20 @@ private:
|
|||||||
double virtualRate;
|
double virtualRate;
|
||||||
|
|
||||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||||
double virtualTempo;
|
double virtualTempo;
|
||||||
|
|
||||||
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
/// Virtual pitch parameter. Effective rate & tempo are calculated from these parameters.
|
||||||
double virtualPitch;
|
double virtualPitch;
|
||||||
|
|
||||||
/// Flag: Has sample rate been set?
|
/// Flag: Has sample rate been set?
|
||||||
bool bSrateSet;
|
bool bSrateSet;
|
||||||
|
|
||||||
/// Accumulator for how many samples in total will be expected as output vs. samples put in,
|
/// Accumulator for how many samples in total will be expected as output vs. samples put in,
|
||||||
/// considering current processing settings.
|
/// considering current processing settings.
|
||||||
double samplesExpectedOut;
|
double samplesExpectedOut;
|
||||||
|
|
||||||
/// Accumulator for how many samples in total have been read out from the processing so far
|
/// Accumulator for how many samples in total have been read out from the processing so far
|
||||||
long samplesOutput;
|
long samplesOutput;
|
||||||
|
|
||||||
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
||||||
/// 'virtualPitch' parameters.
|
/// 'virtualPitch' parameters.
|
||||||
@ -199,28 +199,28 @@ public:
|
|||||||
|
|
||||||
/// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
/// Sets new tempo control value. Normal tempo = 1.0, smaller values
|
||||||
/// represent slower tempo, larger faster tempo.
|
/// represent slower tempo, larger faster tempo.
|
||||||
void setTempo(double newTempo);
|
void setTempo(double newTempo);
|
||||||
|
|
||||||
/// Sets new rate control value as a difference in percents compared
|
/// Sets new rate control value as a difference in percents compared
|
||||||
/// to the original rate (-50 .. +100 %)
|
/// to the original rate (-50 .. +100 %)
|
||||||
void setRateChange(double newRate);
|
void setRateChange(double newRate);
|
||||||
|
|
||||||
/// Sets new tempo control value as a difference in percents compared
|
/// Sets new tempo control value as a difference in percents compared
|
||||||
/// to the original tempo (-50 .. +100 %)
|
/// to the original tempo (-50 .. +100 %)
|
||||||
void setTempoChange(double newTempo);
|
void setTempoChange(double newTempo);
|
||||||
|
|
||||||
/// Sets new pitch control value. Original pitch = 1.0, smaller values
|
/// Sets new pitch control value. Original pitch = 1.0, smaller values
|
||||||
/// represent lower pitches, larger values higher pitch.
|
/// represent lower pitches, larger values higher pitch.
|
||||||
void setPitch(double newPitch);
|
void setPitch(double newPitch);
|
||||||
|
|
||||||
/// Sets pitch change in octaves compared to the original pitch
|
/// Sets pitch change in octaves compared to the original pitch
|
||||||
/// (-1.00 .. +1.00)
|
/// (-1.00 .. +1.00)
|
||||||
void setPitchOctaves(double newPitch);
|
void setPitchOctaves(double newPitch);
|
||||||
|
|
||||||
/// Sets pitch change in semi-tones compared to the original pitch
|
/// Sets pitch change in semi-tones compared to the original pitch
|
||||||
/// (-12 .. +12)
|
/// (-12 .. +12)
|
||||||
void setPitchSemiTones(int newPitch);
|
void setPitchSemiTones(int newPitch);
|
||||||
void setPitchSemiTones(double newPitch);
|
void setPitchSemiTones(double newPitch);
|
||||||
|
|
||||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void setChannels(uint numChannels);
|
void setChannels(uint numChannels);
|
||||||
@ -247,22 +247,22 @@ public:
|
|||||||
///< contains data for both channels.
|
///< contains data for both channels.
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
/// output buffer and removes them from the sample buffer. If there are less than
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
/// 'numsample' samples in the buffer, returns all that available.
|
/// 'numsample' samples in the buffer, returns all that available.
|
||||||
///
|
///
|
||||||
/// \return Number of samples returned.
|
/// \return Number of samples returned.
|
||||||
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
virtual uint receiveSamples(SAMPLETYPE *output, ///< Buffer where to copy output samples.
|
||||||
uint maxSamples ///< How many samples to receive at max.
|
uint maxSamples ///< How many samples to receive at max.
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
/// sample buffer without copying them anywhere.
|
/// sample buffer without copying them anywhere.
|
||||||
///
|
///
|
||||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
/// with 'ptrBegin' function.
|
/// with 'ptrBegin' function.
|
||||||
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
virtual uint receiveSamples(uint maxSamples ///< Remove this many samples from the beginning of pipe.
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Clears all the samples in the object's output and internal processing
|
/// Clears all the samples in the object's output and internal processing
|
||||||
/// buffers.
|
/// buffers.
|
||||||
|
@ -63,7 +63,7 @@ using namespace soundtouch;
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
// Table for the hierarchical mixing position seeking algorithm
|
// Table for the hierarchical mixing position seeking algorithm
|
||||||
static const short _scanOffsets[5][24]={
|
const short _scanOffsets[5][24]={
|
||||||
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
||||||
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
||||||
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
|
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
|
||||||
@ -94,7 +94,9 @@ TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
|||||||
bAutoSeqSetting = true;
|
bAutoSeqSetting = true;
|
||||||
bAutoSeekSetting = true;
|
bAutoSeekSetting = true;
|
||||||
|
|
||||||
// outDebt = 0;
|
maxnorm = 0;
|
||||||
|
maxnormf = 1e8;
|
||||||
|
|
||||||
skipFract = 0;
|
skipFract = 0;
|
||||||
|
|
||||||
tempo = 1.0f;
|
tempo = 1.0f;
|
||||||
@ -282,7 +284,6 @@ inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
||||||
// routine
|
// routine
|
||||||
//
|
//
|
||||||
@ -336,6 +337,11 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
adaptNormalizer();
|
||||||
|
#endif
|
||||||
|
|
||||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||||
clearCrossCorrState();
|
clearCrossCorrState();
|
||||||
|
|
||||||
@ -343,64 +349,161 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Seeks for the optimal overlap-mixing position. The 'stereo' version of the
|
// Quick seek algorithm for improved runtime-performance: First roughly scans through the
|
||||||
// routine
|
// correlation area, and then scan surroundings of two best preliminary correlation candidates
|
||||||
|
// with improved precision
|
||||||
//
|
//
|
||||||
// The best position is determined as the position where the two overlapped
|
// Based on testing:
|
||||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
// - This algorithm gives on average 99% as good match as the full algorith
|
||||||
// value over the overlapping period
|
// - this quick seek algorithm finds the best match on ~90% of cases
|
||||||
|
// - on those 10% of cases when this algorithm doesn't find best match,
|
||||||
|
// it still finds on average ~90% match vs. the best possible match
|
||||||
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||||
{
|
{
|
||||||
int j;
|
#define _MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||||
|
#define SCANSTEP 16
|
||||||
|
#define SCANWIND 8
|
||||||
|
|
||||||
int bestOffs;
|
int bestOffs;
|
||||||
double bestCorr, corr;
|
int i;
|
||||||
int scanCount, corrOffset, tempOffset;
|
int bestOffs2;
|
||||||
|
float bestCorr, corr;
|
||||||
|
float bestCorr2;
|
||||||
|
double norm;
|
||||||
|
|
||||||
|
// note: 'float' types used in this function in case that the platform would need to use software-fp
|
||||||
|
|
||||||
bestCorr = FLT_MIN;
|
bestCorr = FLT_MIN;
|
||||||
bestOffs = _scanOffsets[0][0];
|
bestOffs = SCANWIND;
|
||||||
corrOffset = 0;
|
bestCorr2 = FLT_MIN;
|
||||||
tempOffset = 0;
|
bestOffs2 = 0;
|
||||||
|
|
||||||
// Scans for the best correlation value using four-pass hierarchical search.
|
int best = 0;
|
||||||
|
|
||||||
|
// Scans for the best correlation value by testing each possible position
|
||||||
|
// over the permitted range. Look for two best matches on the first pass to
|
||||||
|
// increase possibility of ideal match.
|
||||||
//
|
//
|
||||||
// The look-up table 'scans' has hierarchical position adjusting steps.
|
// Begin from "SCANSTEP" instead of SCANWIND to make the calculation
|
||||||
// In first pass the routine searhes for the highest correlation with
|
// catch the 'middlepoint' of seekLength vector as that's the a-priori
|
||||||
// relatively coarse steps, then rescans the neighbourhood of the highest
|
// expected best match position
|
||||||
// correlation with better resolution and so on.
|
//
|
||||||
for (scanCount = 0;scanCount < 4; scanCount ++)
|
// Roughly:
|
||||||
|
// - 15% of cases find best result directly on the first round,
|
||||||
|
// - 75% cases find better match on 2nd round around the best match from 1st round
|
||||||
|
// - 10% cases find better match on 2nd round around the 2nd-best-match from 1st round
|
||||||
|
for (i = SCANSTEP; i < seekLength - SCANWIND - 1; i += SCANSTEP)
|
||||||
{
|
{
|
||||||
j = 0;
|
// Calculates correlation value for the mixing position corresponding
|
||||||
while (_scanOffsets[scanCount][j])
|
// to 'i'
|
||||||
|
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||||
|
// heuristic rule to slightly favour values close to mid of the seek range
|
||||||
|
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||||
|
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||||
|
|
||||||
|
// Checks for the highest correlation value
|
||||||
|
if (corr > bestCorr)
|
||||||
{
|
{
|
||||||
double norm;
|
// found new best match. keep the previous best as 2nd best match
|
||||||
tempOffset = corrOffset + _scanOffsets[scanCount][j];
|
bestCorr2 = bestCorr;
|
||||||
if (tempOffset >= seekLength) break;
|
bestOffs2 = bestOffs;
|
||||||
|
bestCorr = corr;
|
||||||
// Calculates correlation value for the mixing position corresponding
|
bestOffs = i;
|
||||||
// to 'tempOffset'
|
}
|
||||||
corr = (double)calcCrossCorr(refPos + channels * tempOffset, pMidBuffer, norm);
|
else if (corr > bestCorr2)
|
||||||
// heuristic rule to slightly favour values close to mid of the range
|
{
|
||||||
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
|
// not new best, but still new 2nd best match
|
||||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
bestCorr2 = corr;
|
||||||
|
bestOffs2 = i;
|
||||||
// Checks for the highest correlation value
|
|
||||||
if (corr > bestCorr)
|
|
||||||
{
|
|
||||||
bestCorr = corr;
|
|
||||||
bestOffs = tempOffset;
|
|
||||||
}
|
|
||||||
j ++;
|
|
||||||
}
|
}
|
||||||
corrOffset = bestOffs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scans surroundings of the found best match with small stepping
|
||||||
|
int end = _MIN(bestOffs + SCANWIND + 1, seekLength);
|
||||||
|
for (i = bestOffs - SCANWIND; i < end; i++)
|
||||||
|
{
|
||||||
|
if (i == bestOffs) continue; // this offset already calculated, thus skip
|
||||||
|
|
||||||
|
// Calculates correlation value for the mixing position corresponding
|
||||||
|
// to 'i'
|
||||||
|
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||||
|
// heuristic rule to slightly favour values close to mid of the range
|
||||||
|
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||||
|
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||||
|
|
||||||
|
// Checks for the highest correlation value
|
||||||
|
if (corr > bestCorr)
|
||||||
|
{
|
||||||
|
bestCorr = corr;
|
||||||
|
bestOffs = i;
|
||||||
|
best = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scans surroundings of the 2nd best match with small stepping
|
||||||
|
end = _MIN(bestOffs2 + SCANWIND + 1, seekLength);
|
||||||
|
for (i = bestOffs2 - SCANWIND; i < end; i++)
|
||||||
|
{
|
||||||
|
if (i == bestOffs2) continue; // this offset already calculated, thus skip
|
||||||
|
|
||||||
|
// Calculates correlation value for the mixing position corresponding
|
||||||
|
// to 'i'
|
||||||
|
corr = (float)calcCrossCorr(refPos + channels*i, pMidBuffer, norm);
|
||||||
|
// heuristic rule to slightly favour values close to mid of the range
|
||||||
|
float tmp = (float)(2 * i - seekLength - 1) / (float)seekLength;
|
||||||
|
corr = ((corr + 0.1f) * (1.0f - 0.25f * tmp * tmp));
|
||||||
|
|
||||||
|
// Checks for the highest correlation value
|
||||||
|
if (corr > bestCorr)
|
||||||
|
{
|
||||||
|
bestCorr = corr;
|
||||||
|
bestOffs = i;
|
||||||
|
best = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
// clear cross correlation routine state if necessary (is so e.g. in MMX routines).
|
||||||
clearCrossCorrState();
|
clearCrossCorrState();
|
||||||
|
|
||||||
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
adaptNormalizer();
|
||||||
|
#endif
|
||||||
|
|
||||||
return bestOffs;
|
return bestOffs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// For integer algorithm: adapt normalization factor divider with music so that
|
||||||
|
/// it'll not be pessimistically restrictive that can degrade quality on quieter sections
|
||||||
|
/// yet won't cause integer overflows either
|
||||||
|
void TDStretch::adaptNormalizer()
|
||||||
|
{
|
||||||
|
// Do not adapt normalizer over too silent sequences to avoid averaging filter depleting to
|
||||||
|
// too low values during pauses in music
|
||||||
|
if ((maxnorm > 1000) || (maxnormf > 40000000))
|
||||||
|
{
|
||||||
|
//norm averaging filter
|
||||||
|
maxnormf = 0.9f * maxnormf + 0.1f * (float)maxnorm;
|
||||||
|
|
||||||
|
if ((maxnorm > 800000000) && (overlapDividerBitsNorm < 16))
|
||||||
|
{
|
||||||
|
// large values, so increase divider
|
||||||
|
overlapDividerBitsNorm++;
|
||||||
|
if (maxnorm > 1600000000) overlapDividerBitsNorm++; // extra large value => extra increase
|
||||||
|
}
|
||||||
|
else if ((maxnormf < 1000000) && (overlapDividerBitsNorm > 0))
|
||||||
|
{
|
||||||
|
// extra small values, decrease divider
|
||||||
|
overlapDividerBitsNorm--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxnorm = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// clear cross correlation routine state if necessary
|
/// clear cross correlation routine state if necessary
|
||||||
void TDStretch::clearCrossCorrState()
|
void TDStretch::clearCrossCorrState()
|
||||||
{
|
{
|
||||||
@ -422,7 +525,7 @@ void TDStretch::calcSeqParameters()
|
|||||||
#define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEQ_K ((AUTOSEQ_AT_MAX - AUTOSEQ_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
||||||
#define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEQ_C (AUTOSEQ_AT_MIN - (AUTOSEQ_K) * (AUTOSEQ_TEMPO_LOW))
|
||||||
|
|
||||||
// seek-window-ms setting values at above low & top tempo
|
// seek-window-ms setting values at above low & top tempoq
|
||||||
#define AUTOSEEK_AT_MIN 25.0
|
#define AUTOSEEK_AT_MIN 25.0
|
||||||
#define AUTOSEEK_AT_MAX 15.0
|
#define AUTOSEEK_AT_MAX 15.0
|
||||||
#define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
#define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
|
||||||
@ -736,13 +839,15 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
|||||||
// calculate overlap length so that it's power of 2 - thus it's easy to do
|
// calculate overlap length so that it's power of 2 - thus it's easy to do
|
||||||
// integer division by right-shifting. Term "-1" at end is to account for
|
// integer division by right-shifting. Term "-1" at end is to account for
|
||||||
// the extra most significatnt bit left unused in result by signed multiplication
|
// the extra most significatnt bit left unused in result by signed multiplication
|
||||||
overlapDividerBits = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
overlapDividerBitsPure = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
||||||
if (overlapDividerBits > 9) overlapDividerBits = 9;
|
if (overlapDividerBitsPure > 9) overlapDividerBitsPure = 9;
|
||||||
if (overlapDividerBits < 3) overlapDividerBits = 3;
|
if (overlapDividerBitsPure < 3) overlapDividerBitsPure = 3;
|
||||||
newOvl = (int)pow(2.0, (int)overlapDividerBits + 1); // +1 => account for -1 above
|
newOvl = (int)pow(2.0, (int)overlapDividerBitsPure + 1); // +1 => account for -1 above
|
||||||
|
|
||||||
acceptNewOverlapLength(newOvl);
|
acceptNewOverlapLength(newOvl);
|
||||||
|
|
||||||
|
overlapDividerBitsNorm = overlapDividerBitsPure;
|
||||||
|
|
||||||
// calculate sloping divider so that crosscorrelation operation won't
|
// calculate sloping divider so that crosscorrelation operation won't
|
||||||
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
||||||
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
||||||
@ -750,10 +855,10 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, double &norm) const
|
double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, double &norm)
|
||||||
{
|
{
|
||||||
long corr;
|
long corr;
|
||||||
long lnorm;
|
unsigned long lnorm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
corr = lnorm = 0;
|
corr = lnorm = 0;
|
||||||
@ -763,15 +868,19 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
|
|||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
for (i = 0; i < channels * overlapLength; i += 4)
|
||||||
{
|
{
|
||||||
corr += (mixingPos[i] * compare[i] +
|
corr += (mixingPos[i] * compare[i] +
|
||||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow
|
||||||
corr += (mixingPos[i + 2] * compare[i + 2] +
|
corr += (mixingPos[i + 2] * compare[i + 2] +
|
||||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBits;
|
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBitsNorm;
|
||||||
lnorm += (mixingPos[i] * mixingPos[i] +
|
lnorm += (mixingPos[i] * mixingPos[i] +
|
||||||
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow
|
||||||
lnorm += (mixingPos[i + 2] * mixingPos[i + 2] +
|
lnorm += (mixingPos[i + 2] * mixingPos[i + 2] +
|
||||||
mixingPos[i + 3] * mixingPos[i + 3]) >> overlapDividerBits;
|
mixingPos[i + 3] * mixingPos[i + 3]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lnorm > maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = lnorm;
|
||||||
|
}
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
norm = (double)lnorm;
|
norm = (double)lnorm;
|
||||||
@ -780,17 +889,17 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
|
|||||||
|
|
||||||
|
|
||||||
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) const
|
double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm)
|
||||||
{
|
{
|
||||||
long corr;
|
long corr;
|
||||||
long lnorm;
|
unsigned long lnorm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
// cancel first normalizer tap from previous round
|
// cancel first normalizer tap from previous round
|
||||||
lnorm = 0;
|
lnorm = 0;
|
||||||
for (i = 1; i <= channels; i ++)
|
for (i = 1; i <= channels; i ++)
|
||||||
{
|
{
|
||||||
lnorm -= (mixingPos[-i] * mixingPos[-i]) >> overlapDividerBits;
|
lnorm -= (mixingPos[-i] * mixingPos[-i]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
corr = 0;
|
corr = 0;
|
||||||
@ -800,18 +909,23 @@ double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *c
|
|||||||
for (i = 0; i < channels * overlapLength; i += 4)
|
for (i = 0; i < channels * overlapLength; i += 4)
|
||||||
{
|
{
|
||||||
corr += (mixingPos[i] * compare[i] +
|
corr += (mixingPos[i] * compare[i] +
|
||||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits; // notice: do intermediate division here to avoid integer overflow
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm; // notice: do intermediate division here to avoid integer overflow
|
||||||
corr += (mixingPos[i + 2] * compare[i + 2] +
|
corr += (mixingPos[i + 2] * compare[i + 2] +
|
||||||
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBits;
|
mixingPos[i + 3] * compare[i + 3]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update normalizer with last samples of this round
|
// update normalizer with last samples of this round
|
||||||
for (int j = 0; j < channels; j ++)
|
for (int j = 0; j < channels; j ++)
|
||||||
{
|
{
|
||||||
i --;
|
i --;
|
||||||
lnorm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBits;
|
lnorm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
norm += (double)lnorm;
|
norm += (double)lnorm;
|
||||||
|
if (norm > maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = (unsigned long)norm;
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
@ -896,7 +1010,7 @@ void TDStretch::calculateOverlapLength(int overlapInMsec)
|
|||||||
|
|
||||||
|
|
||||||
/// Calculate cross-correlation
|
/// Calculate cross-correlation
|
||||||
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &anorm) const
|
double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, double &anorm)
|
||||||
{
|
{
|
||||||
double corr;
|
double corr;
|
||||||
double norm;
|
double norm;
|
||||||
@ -927,7 +1041,7 @@ double TDStretch::calcCrossCorr(const float *mixingPos, const float *compare, do
|
|||||||
|
|
||||||
|
|
||||||
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) const
|
double TDStretch::calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm)
|
||||||
{
|
{
|
||||||
double corr;
|
double corr;
|
||||||
int i;
|
int i;
|
||||||
|
@ -112,39 +112,46 @@ class TDStretch : public FIFOProcessor
|
|||||||
protected:
|
protected:
|
||||||
int channels;
|
int channels;
|
||||||
int sampleReq;
|
int sampleReq;
|
||||||
double tempo;
|
|
||||||
|
|
||||||
SAMPLETYPE *pMidBuffer;
|
|
||||||
SAMPLETYPE *pMidBufferUnaligned;
|
|
||||||
int overlapLength;
|
int overlapLength;
|
||||||
int seekLength;
|
int seekLength;
|
||||||
int seekWindowLength;
|
int seekWindowLength;
|
||||||
int overlapDividerBits;
|
int overlapDividerBitsNorm;
|
||||||
|
int overlapDividerBitsPure;
|
||||||
int slopingDivider;
|
int slopingDivider;
|
||||||
double nominalSkip;
|
|
||||||
double skipFract;
|
|
||||||
FIFOSampleBuffer outputBuffer;
|
|
||||||
FIFOSampleBuffer inputBuffer;
|
|
||||||
bool bQuickSeek;
|
|
||||||
|
|
||||||
int sampleRate;
|
int sampleRate;
|
||||||
int sequenceMs;
|
int sequenceMs;
|
||||||
int seekWindowMs;
|
int seekWindowMs;
|
||||||
int overlapMs;
|
int overlapMs;
|
||||||
|
|
||||||
|
unsigned long maxnorm;
|
||||||
|
float maxnormf;
|
||||||
|
|
||||||
|
double tempo;
|
||||||
|
double nominalSkip;
|
||||||
|
double skipFract;
|
||||||
|
|
||||||
|
bool bQuickSeek;
|
||||||
bool bAutoSeqSetting;
|
bool bAutoSeqSetting;
|
||||||
bool bAutoSeekSetting;
|
bool bAutoSeekSetting;
|
||||||
|
|
||||||
|
SAMPLETYPE *pMidBuffer;
|
||||||
|
SAMPLETYPE *pMidBufferUnaligned;
|
||||||
|
|
||||||
|
FIFOSampleBuffer outputBuffer;
|
||||||
|
FIFOSampleBuffer inputBuffer;
|
||||||
|
|
||||||
void acceptNewOverlapLength(int newOverlapLength);
|
void acceptNewOverlapLength(int newOverlapLength);
|
||||||
|
|
||||||
virtual void clearCrossCorrState();
|
virtual void clearCrossCorrState();
|
||||||
void calculateOverlapLength(int overlapMs);
|
void calculateOverlapLength(int overlapMs);
|
||||||
|
|
||||||
virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm) const;
|
virtual double calcCrossCorr(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm);
|
||||||
virtual double calcCrossCorrAccumulate(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm) const;
|
virtual double calcCrossCorrAccumulate(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare, double &norm);
|
||||||
|
|
||||||
virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPositionFull(const SAMPLETYPE *refPos);
|
||||||
virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPositionQuick(const SAMPLETYPE *refPos);
|
||||||
int seekBestOverlapPosition(const SAMPLETYPE *refPos);
|
virtual int seekBestOverlapPosition(const SAMPLETYPE *refPos);
|
||||||
|
|
||||||
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||||
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
|
||||||
@ -154,6 +161,8 @@ protected:
|
|||||||
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
|
void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const;
|
||||||
|
|
||||||
void calcSeqParameters();
|
void calcSeqParameters();
|
||||||
|
void adaptNormalizer();
|
||||||
|
|
||||||
|
|
||||||
/// Changes the tempo of the given sound samples.
|
/// Changes the tempo of the given sound samples.
|
||||||
/// Returns amount of samples returned in the "output" buffer.
|
/// Returns amount of samples returned in the "output" buffer.
|
||||||
@ -249,8 +258,8 @@ public:
|
|||||||
class TDStretchMMX : public TDStretch
|
class TDStretchMMX : public TDStretch
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double calcCrossCorr(const short *mixingPos, const short *compare, double &norm) const;
|
double calcCrossCorr(const short *mixingPos, const short *compare, double &norm);
|
||||||
double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm) const;
|
double calcCrossCorrAccumulate(const short *mixingPos, const short *compare, double &norm);
|
||||||
virtual void overlapStereo(short *output, const short *input) const;
|
virtual void overlapStereo(short *output, const short *input) const;
|
||||||
virtual void clearCrossCorrState();
|
virtual void clearCrossCorrState();
|
||||||
};
|
};
|
||||||
@ -262,8 +271,8 @@ public:
|
|||||||
class TDStretchSSE : public TDStretch
|
class TDStretchSSE : public TDStretch
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double calcCrossCorr(const float *mixingPos, const float *compare, double &norm) const;
|
double calcCrossCorr(const float *mixingPos, const float *compare, double &norm);
|
||||||
double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm) const;
|
double calcCrossCorrAccumulate(const float *mixingPos, const float *compare, double &norm);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /// SOUNDTOUCH_ALLOW_SSE
|
#endif /// SOUNDTOUCH_ALLOW_SSE
|
||||||
|
@ -68,7 +68,7 @@ using namespace soundtouch;
|
|||||||
|
|
||||||
|
|
||||||
// Calculates cross correlation of two buffers
|
// Calculates cross correlation of two buffers
|
||||||
double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &dnorm) const
|
double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &dnorm)
|
||||||
{
|
{
|
||||||
const __m64 *pVec1, *pVec2;
|
const __m64 *pVec1, *pVec2;
|
||||||
__m64 shifter;
|
__m64 shifter;
|
||||||
@ -79,7 +79,7 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
|||||||
pVec1 = (__m64*)pV1;
|
pVec1 = (__m64*)pV1;
|
||||||
pVec2 = (__m64*)pV2;
|
pVec2 = (__m64*)pV2;
|
||||||
|
|
||||||
shifter = _m_from_int(overlapDividerBits);
|
shifter = _m_from_int(overlapDividerBitsNorm);
|
||||||
normaccu = accu = _mm_setzero_si64();
|
normaccu = accu = _mm_setzero_si64();
|
||||||
|
|
||||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||||
@ -123,6 +123,11 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
|||||||
// Clear MMS state
|
// Clear MMS state
|
||||||
_m_empty();
|
_m_empty();
|
||||||
|
|
||||||
|
if (norm > (long)maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = norm;
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
dnorm = (double)norm;
|
dnorm = (double)norm;
|
||||||
@ -134,7 +139,7 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
|||||||
|
|
||||||
|
|
||||||
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
/// Update cross-correlation by accumulating "norm" coefficient by previously calculated value
|
||||||
double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2, double &dnorm) const
|
double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2, double &dnorm)
|
||||||
{
|
{
|
||||||
const __m64 *pVec1, *pVec2;
|
const __m64 *pVec1, *pVec2;
|
||||||
__m64 shifter;
|
__m64 shifter;
|
||||||
@ -146,13 +151,13 @@ double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2,
|
|||||||
lnorm = 0;
|
lnorm = 0;
|
||||||
for (i = 1; i <= channels; i ++)
|
for (i = 1; i <= channels; i ++)
|
||||||
{
|
{
|
||||||
lnorm -= (pV1[-i] * pV1[-i]) >> overlapDividerBits;
|
lnorm -= (pV1[-i] * pV1[-i]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
pVec1 = (__m64*)pV1;
|
pVec1 = (__m64*)pV1;
|
||||||
pVec2 = (__m64*)pV2;
|
pVec2 = (__m64*)pV2;
|
||||||
|
|
||||||
shifter = _m_from_int(overlapDividerBits);
|
shifter = _m_from_int(overlapDividerBitsNorm);
|
||||||
accu = _mm_setzero_si64();
|
accu = _mm_setzero_si64();
|
||||||
|
|
||||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||||
@ -191,10 +196,15 @@ double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2,
|
|||||||
pV1 = (short *)pVec1;
|
pV1 = (short *)pVec1;
|
||||||
for (int j = 1; j <= channels; j ++)
|
for (int j = 1; j <= channels; j ++)
|
||||||
{
|
{
|
||||||
lnorm += (pV1[-j] * pV1[-j]) >> overlapDividerBits;
|
lnorm += (pV1[-j] * pV1[-j]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
dnorm += (double)lnorm;
|
dnorm += (double)lnorm;
|
||||||
|
|
||||||
|
if (lnorm > (long)maxnorm)
|
||||||
|
{
|
||||||
|
maxnorm = lnorm;
|
||||||
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
return (double)corr / sqrt((dnorm < 1e-9) ? 1.0 : dnorm);
|
return (double)corr / sqrt((dnorm < 1e-9) ? 1.0 : dnorm);
|
||||||
@ -233,7 +243,7 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
|||||||
|
|
||||||
// Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
|
// Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
|
||||||
// overlapDividerBits calculation earlier.
|
// overlapDividerBits calculation earlier.
|
||||||
shifter = _m_from_int(overlapDividerBits + 1);
|
shifter = _m_from_int(overlapDividerBitsPure + 1);
|
||||||
|
|
||||||
for (i = 0; i < overlapLength / 4; i ++)
|
for (i = 0; i < overlapLength / 4; i ++)
|
||||||
{
|
{
|
||||||
|
@ -71,7 +71,7 @@ using namespace soundtouch;
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
// Calculates cross correlation of two buffers
|
// Calculates cross correlation of two buffers
|
||||||
double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &anorm) const
|
double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &anorm)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
const float *pVec1;
|
const float *pVec1;
|
||||||
@ -183,7 +183,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, double &norm) const
|
double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, double &norm)
|
||||||
{
|
{
|
||||||
// call usual calcCrossCorr function because SSE does not show big benefit of
|
// call usual calcCrossCorr function because SSE does not show big benefit of
|
||||||
// accumulating "norm" value, and also the "norm" rolling algorithm would get
|
// accumulating "norm" value, and also the "norm" rolling algorithm would get
|
||||||
|
Loading…
Reference in New Issue
Block a user