Updated the SoundTouch library to version 1.5.

It works fine and without further modifications in Windows, for SPU2-X and ZeroSPU2.
Linux is untested, but hopefully also fine :p

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2734 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
ramapcsx2 2010-03-18 20:04:51 +00:00
parent 70a3940d75
commit ccfb4e1ea9
27 changed files with 1635 additions and 729 deletions

View File

@ -24,7 +24,7 @@
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
/// 6.0 processor pack" update to support 3DNow! instruction set. The update is
/// available for download at Microsoft Developers Network, see here:
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
///
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
/// perform a search with keywords "processor pack".
@ -35,10 +35,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.10 $
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
// File revision : $Revision: 4 $
//
// $Id: 3dnow_win.cpp,v 1.10 2006/02/05 16:44:06 Olli Exp $
// $Id: 3dnow_win.cpp 63 2009-02-21 16:00:14Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -66,7 +66,7 @@
#include "cpu_detect.h"
#include "STTypes.h"
#ifndef _WIN32
#ifndef WIN32
#error "wrong platform - this source code file is exclusively for Win32 platform"
#endif
@ -82,17 +82,13 @@ using namespace soundtouch;
//////////////////////////////////////////////////////////////////////////////
#include "TDStretch.h"
#include <limits.h>
// these are declared in 'TDStretch.cpp'
extern int scanOffsets[4][24];
// Calculates cross correlation of two buffers
double TDStretch3DNow::calcCrossCorrStereo(const float *pV1, const float *pV2) const
{
uint overlapLengthLocal = overlapLength;
float corr;
int overlapLengthLocal = overlapLength;
float corr = 0;
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
/*
@ -181,12 +177,15 @@ double TDStretch3DNow::calcCrossCorrStereo(const float *pV1, const float *pV2) c
FIRFilter3DNow::FIRFilter3DNow() : FIRFilter()
{
filterCoeffsUnalign = NULL;
filterCoeffsAlign = NULL;
}
FIRFilter3DNow::~FIRFilter3DNow()
{
delete[] filterCoeffsUnalign;
filterCoeffsUnalign = NULL;
filterCoeffsAlign = NULL;
}
@ -203,7 +202,7 @@ void FIRFilter3DNow::setCoefficients(const float *coeffs, uint newLength, uint u
// Ensure that filter coeffs array is aligned to 16-byte boundary
delete[] filterCoeffsUnalign;
filterCoeffsUnalign = new float[2 * newLength + 4];
filterCoeffsAlign = (float *)(((uint)filterCoeffsUnalign + 15) & -16);
filterCoeffsAlign = (float *)(((uint)filterCoeffsUnalign + 15) & (uint)-16);
fDivider = (float)resultDivider;
@ -217,10 +216,10 @@ void FIRFilter3DNow::setCoefficients(const float *coeffs, uint newLength, uint u
// 3DNow!-optimized version of the filter routine for stereo sound
uint FIRFilter3DNow::evaluateFilterStereo(float *dest, const float *src, const uint numSamples) const
uint FIRFilter3DNow::evaluateFilterStereo(float *dest, const float *src, uint numSamples) const
{
float *filterCoeffsLocal = filterCoeffsAlign;
uint count = (numSamples - length) & -2;
uint count = (numSamples - length) & (uint)-2;
uint lengthLocal = length / 4;
assert(length != 0);

View File

@ -12,10 +12,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.9 $
// Last changed : $Date: 2009-01-11 13:34:24 +0200 (Sun, 11 Jan 2009) $
// File revision : $Revision: 4 $
//
// $Id: AAFilter.cpp,v 1.9 2006/02/05 16:44:06 Olli Exp $
// $Id: AAFilter.cpp 45 2009-01-11 11:34:24Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -58,11 +58,11 @@ using namespace soundtouch;
*
*****************************************************************************/
AAFilter::AAFilter(const uint length)
AAFilter::AAFilter(uint len)
{
pFIR = FIRFilter::newInstance();
cutoffFreq = 0.5;
setLength(length);
setLength(len);
}
@ -77,7 +77,7 @@ AAFilter::~AAFilter()
// Sets new anti-alias filter cut-off edge frequency, scaled to
// sampling frequency (nyquist frequency = 0.5).
// The filter will cut frequencies higher than the given frequency.
void AAFilter::setCutoffFreq(const double newCutoffFreq)
void AAFilter::setCutoffFreq(double newCutoffFreq)
{
cutoffFreq = newCutoffFreq;
calculateCoeffs();
@ -86,7 +86,7 @@ void AAFilter::setCutoffFreq(const double newCutoffFreq)
// Sets number of FIR filter taps
void AAFilter::setLength(const uint newLength)
void AAFilter::setLength(uint newLength)
{
length = newLength;
calculateCoeffs();
@ -104,7 +104,7 @@ void AAFilter::calculateCoeffs()
double *work;
SAMPLETYPE *coeffs;
assert(length > 0);
assert(length >= 2);
assert(length % 4 == 0);
assert(cutoffFreq >= 0);
assert(cutoffFreq <= 0.5);

View File

@ -13,10 +13,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.10 $
// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $
// File revision : $Revision: 4 $
//
// $Id: AAFilter.h,v 1.10 2006/02/05 16:44:06 Olli Exp $
// $Id: AAFilter.h 11 2008-02-10 16:26:55Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//

View File

@ -26,10 +26,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.5 $
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
// File revision : $Revision: 4 $
//
// $Id: BPMDetect.h,v 1.5 2006/02/05 16:44:06 Olli Exp $
// $Id: BPMDetect.h 63 2009-02-21 16:00:14Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -60,8 +60,11 @@
#include "STTypes.h"
#include "FIFOSampleBuffer.h"
namespace soundtouch
{
/// Minimum allowed BPM rate. Used to restrict accepted result above a reasonable limit.
#define MIN_BPM 45
#define MIN_BPM 29
/// Maximum allowed BPM rate. Used to restrict accepted result below a reasonable limit.
#define MAX_BPM 230
@ -105,9 +108,6 @@ protected:
/// FIFO-buffer for decimated processing samples.
soundtouch::FIFOSampleBuffer *buffer;
/// Initialize the class for processing.
void init(int numChannels, int sampleRate);
/// Updates auto-correlation function for given number of decimated samples that
/// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
/// though).
@ -143,8 +143,8 @@ public:
/// function.
///
/// Notice that data in 'samples' array can be disrupted in processing.
void inputSamples(soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer
int numSamples ///< Number of samples in buffer
void inputSamples(const soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer
int numSamples ///< Number of samples in buffer
);
@ -156,4 +156,6 @@ public:
float getBpm();
};
}
#endif // _BPMDetect_H_

View File

@ -15,10 +15,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.11 $
// Last changed : $Date: 2009-02-27 19:24:42 +0200 (Fri, 27 Feb 2009) $
// File revision : $Revision: 4 $
//
// $Id: FIFOSampleBuffer.cpp,v 1.11 2006/02/05 16:44:06 Olli Exp $
// $Id: FIFOSampleBuffer.cpp 68 2009-02-27 17:24:42Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -54,14 +54,16 @@
using namespace soundtouch;
// Constructor
FIFOSampleBuffer::FIFOSampleBuffer(uint numChannels)
FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
{
assert(numChannels > 0);
sizeInBytes = 0; // reasonable initial value
buffer = NULL; //new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE)];
buffer = NULL;
bufferUnaligned = NULL;
samplesInBuffer = 0;
bufferPos = 0;
channels = numChannels;
channels = (uint)numChannels;
ensureCapacity(32); // allocate initial capacity
}
@ -69,16 +71,19 @@ FIFOSampleBuffer::FIFOSampleBuffer(uint numChannels)
FIFOSampleBuffer::~FIFOSampleBuffer()
{
delete[] bufferUnaligned;
bufferUnaligned = NULL;
buffer = NULL;
}
// Sets number of channels, 1 = mono, 2 = stereo
void FIFOSampleBuffer::setChannels(const uint numChannels)
void FIFOSampleBuffer::setChannels(int numChannels)
{
uint usedBytes;
assert(numChannels > 0);
usedBytes = channels * samplesInBuffer;
channels = numChannels;
channels = (uint)numChannels;
samplesInBuffer = usedBytes / channels;
}
@ -88,7 +93,7 @@ void FIFOSampleBuffer::setChannels(const uint numChannels)
// location on to the beginning of the buffer.
void FIFOSampleBuffer::rewind()
{
if (bufferPos)
if (buffer && bufferPos)
{
memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
bufferPos = 0;
@ -98,10 +103,10 @@ void FIFOSampleBuffer::rewind()
// Adds 'numSamples' pcs of samples from the 'samples' memory position to
// the sample buffer.
void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint numSamples)
void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples)
{
memcpy(ptrEnd(numSamples), samples, sizeof(SAMPLETYPE) * numSamples * channels);
samplesInBuffer += numSamples;
memcpy(ptrEnd(nSamples), samples, sizeof(SAMPLETYPE) * nSamples * channels);
samplesInBuffer += nSamples;
}
@ -111,13 +116,13 @@ void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint numSamples)
// This function is used to update the number of samples in the sample buffer
// when accessing the buffer directly with 'ptrEnd' function. Please be
// careful though!
void FIFOSampleBuffer::putSamples(uint numSamples)
void FIFOSampleBuffer::putSamples(uint nSamples)
{
uint req;
req = samplesInBuffer + numSamples;
req = samplesInBuffer + nSamples;
ensureCapacity(req);
samplesInBuffer += numSamples;
samplesInBuffer += nSamples;
}
@ -147,8 +152,9 @@ SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
// When using this function to output samples, also remember to 'remove' the
// outputted samples from the buffer by calling the
// 'receiveSamples(numSamples)' function
SAMPLETYPE *FIFOSampleBuffer::ptrBegin() const
SAMPLETYPE *FIFOSampleBuffer::ptrBegin()
{
assert(buffer);
return buffer + bufferPos * channels;
}
@ -164,15 +170,19 @@ void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
if (capacityRequirement > getCapacity())
{
// enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & -4096;
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
assert(sizeInBytes % 2 == 0);
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
if (tempUnaligned == NULL)
{
throw std::runtime_error("Couldn't allocate memory!\n");
}
temp = (SAMPLETYPE *)(((ulongptr)tempUnaligned + 15) & -16);
memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
// Align the buffer to begin at 16byte cache line boundary for optimal performance
temp = (SAMPLETYPE *)(((ulong)tempUnaligned + 15) & (ulong)-16);
if (samplesInBuffer)
{
memcpy(temp, ptrBegin(), samplesInBuffer * channels * sizeof(SAMPLETYPE));
}
delete[] bufferUnaligned;
buffer = temp;
bufferUnaligned = tempUnaligned;

View File

@ -15,10 +15,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.9 $
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
// File revision : $Revision: 4 $
//
// $Id: FIFOSampleBuffer.h,v 1.9 2006/02/05 16:44:06 Olli Exp $
// $Id: FIFOSampleBuffer.h 63 2009-02-21 16:00:14Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -85,7 +85,7 @@ private:
void rewind();
/// Ensures that the buffer has capacity for at least this many samples.
void ensureCapacity(const uint capacityRequirement);
void ensureCapacity(uint capacityRequirement);
/// Returns current capacity.
uint getCapacity() const;
@ -93,7 +93,7 @@ private:
public:
/// Constructor
FIFOSampleBuffer(uint numChannels = 2 ///< Number of channels, 1=mono, 2=stereo.
FIFOSampleBuffer(int numChannels = 2 ///< Number of channels, 1=mono, 2=stereo.
///< Default is stereo.
);
@ -107,7 +107,7 @@ public:
/// When using this function to output samples, also remember to 'remove' the
/// output samples from the buffer by calling the
/// 'receiveSamples(numSamples)' function
virtual SAMPLETYPE *ptrBegin() const;
virtual SAMPLETYPE *ptrBegin();
/// Returns a pointer to the end of the used part of the sample buffer (i.e.
/// where the new samples are to be inserted). This function may be used for
@ -160,7 +160,7 @@ public:
virtual uint numSamples() const;
/// Sets number of channels, 1 = mono, 2 = stereo.
void setChannels(uint numChannels);
void setChannels(int numChannels);
/// Returns nonzero if there aren't any samples available for outputting.
virtual int isEmpty() const;

View File

@ -17,10 +17,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.8 $
// Last changed : $Date: 2009-04-13 16:18:48 +0300 (Mon, 13 Apr 2009) $
// File revision : $Revision: 4 $
//
// $Id: FIFOSamplePipe.h,v 1.8 2006/02/05 16:44:06 Olli Exp $
// $Id: FIFOSamplePipe.h 69 2009-04-13 13:18:48Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -59,6 +59,10 @@ namespace soundtouch
class FIFOSamplePipe
{
public:
// virtual default destructor
virtual ~FIFOSamplePipe() {}
/// Returns a pointer to the beginning of the output samples.
/// This function is provided for accessing the output samples directly.
/// Please be careful for not to corrupt the book-keeping!
@ -66,12 +70,12 @@ public:
/// When using this function to output samples, also remember to 'remove' the
/// output samples from the buffer by calling the
/// 'receiveSamples(numSamples)' function
virtual SAMPLETYPE *ptrBegin() const = 0;
virtual SAMPLETYPE *ptrBegin() = 0;
/// Adds 'numSamples' pcs of samples from the 'samples' memory position to
/// the sample buffer.
virtual void putSamples(const SAMPLETYPE *samples, ///< Pointer to samples.
uint numSamples ///< Number of samples to insert.
uint numSamples ///< Number of samples to insert.
) = 0;
@ -166,7 +170,7 @@ protected:
/// When using this function to output samples, also remember to 'remove' the
/// output samples from the buffer by calling the
/// 'receiveSamples(numSamples)' function
virtual SAMPLETYPE *ptrBegin() const
virtual SAMPLETYPE *ptrBegin()
{
return output->ptrBegin();
}

View File

@ -11,10 +11,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.16 $
// Last changed : $Date: 2009-02-25 19:13:51 +0200 (Wed, 25 Feb 2009) $
// File revision : $Revision: 4 $
//
// $Id: FIRFilter.cpp,v 1.16 2006/02/05 16:44:06 Olli Exp $
// $Id: FIRFilter.cpp 67 2009-02-25 17:13:51Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -58,6 +58,7 @@ using namespace soundtouch;
FIRFilter::FIRFilter()
{
resultDivFactor = 0;
resultDivider = 0;
length = 0;
lengthDiv8 = 0;
filterCoeffs = NULL;
@ -81,6 +82,9 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
#endif
assert(length != 0);
assert(src != NULL);
assert(dest != NULL);
assert(filterCoeffs != NULL);
end = 2 * (numSamples - length);
@ -177,11 +181,7 @@ void FIRFilter::setCoefficients(const SAMPLETYPE *coeffs, uint newLength, uint u
assert(length == newLength);
resultDivFactor = uResultDivFactor;
#ifdef INTEGER_SAMPLES
resultDivider = (SAMPLETYPE)(1<<resultDivFactor);
#else
resultDivider = (SAMPLETYPE)powf(2, (SAMPLETYPE)resultDivFactor);
#endif
resultDivider = (SAMPLETYPE)::pow(2.0, (int)resultDivFactor);
delete[] filterCoeffs;
filterCoeffs = new SAMPLETYPE[length];
@ -207,7 +207,6 @@ uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSample
assert(length > 0);
assert(lengthDiv8 * 8 == length);
if (numSamples < length) return 0;
assert(resultDivFactor >= 0);
if (numChannels == 2)
{
return evaluateFilterStereo(dest, src, numSamples);
@ -223,18 +222,16 @@ uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSample
void * FIRFilter::operator new(size_t s)
{
// Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
throw std::runtime_error("Don't use 'new FIRFilter', use 'newInstance' member instead!");
throw std::runtime_error("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!");
return NULL;
}
FIRFilter * FIRFilter::newInstance()
{
uint uExtensions = 0;
uint uExtensions;
#if !defined(_MSC_VER) || !defined(__x86_64__)
uExtensions = detectCPUextensions();
#endif
// Check if MMX/SSE/3DNow! instruction set extensions supported by CPU

View File

@ -11,10 +11,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.17 $
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
// File revision : $Revision: 4 $
//
// $Id: FIRFilter.h,v 1.17 2006/02/05 16:44:06 Olli Exp $
// $Id: FIRFilter.h 63 2009-02-21 16:00:14Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -42,6 +42,7 @@
#ifndef FIRFilter_H
#define FIRFilter_H
#include <stddef.h>
#include "STTypes.h"
namespace soundtouch
@ -77,7 +78,7 @@ public:
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
/// depending on if we've a MMX-capable CPU available or not.
void * operator new(size_t s);
static void * operator new(size_t s);
static FIRFilter *newInstance();
@ -103,7 +104,7 @@ public:
#ifdef ALLOW_MMX
/// Class that implements MMX optimized functions exclusive for 16bit integer samples type.
/// Class that implements MMX optimized functions exclusive for 16bit integer samples type.
class FIRFilterMMX : public FIRFilter
{
protected:

752
3rdparty/SoundTouch/README.html vendored Normal file
View File

@ -0,0 +1,752 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=windows-1252">
<meta http-equiv="Content-Language" content="en-us">
<meta name="author" content="Olli Parviainen">
<meta name="description"
content="Readme file for SoundTouch audio processing library">
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
<meta name="ProgId" content="FrontPage.Editor.Document">
<title>SoundTouch library README</title>
<style>
<!--
.normal { font-family: Arial }
-->
</style>
</head>
<body class="normal">
<hr>
<h1>SoundTouch audio processing library v1.5.0
</h1>
<p class="normal">SoundTouch library Copyright (c) Olli
Parviainen 2002-2009 </p>
<hr>
<h2>1. Introduction </h2>
<p>SoundTouch is an open-source audio
processing library that allows changing the sound tempo, pitch
and playback rate parameters independently from each other, i.e.:</p>
<ul>
<li>Sound tempo can be increased or decreased while
maintaining the original pitch</li>
<li>Sound pitch can be increased or decreased while
maintaining the original tempo </li>
<li>Change playback rate that affects both tempo
and pitch at the same time </li>
<li>Choose any combination of tempo/pitch/rate</li>
</ul>
<h3>1.1 Contact information </h3>
<p>Author email: oparviai 'at' iki.fi </p>
<p>SoundTouch WWW page: <a href="http://www.surina.net/soundtouch">http://www.surina.net/soundtouch</a></p>
<hr>
<h2>2. Compiling SoundTouch</h2>
<p>Before compiling, notice that you can choose the sample data format
if it's desirable to use floating point sample
data instead of 16bit integers. See section "sample data format"
for more information.</p>
<h3>2.1. Building in Microsoft Windows</h3>
<p>Project files for Microsoft Visual C++ 6.0 and Visual C++ .NET are
supplied with the source code package.&nbsp;</p>
<p> Please notice that SoundTouch
library uses processor-specific optimizations for Pentium III and AMD
processors. Visual Studio .NET and later versions supports the required
instructions by default, but Visual Studio 6.0 requires a processor pack upgrade
to be installed in order to support these optimizations. The processor pack upgrade can be downloaded from
Microsoft site at this URL:</p>
<p><a href="http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx">http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx</a></p>
<p>If the above URL is unavailable or removed, go
to <a href="http://msdn.microsoft.com/">http://msdn.microsoft.com</a>
and perform a search with keywords &quot;processor pack&quot;. </p>
<p>To build the binaries with Visual C++
compiler, either run &quot;make-win.bat&quot; script, or open the
appropriate project files in source code directories with Visual
Studio. The final executable will appear under the &quot;SoundTouch\bin&quot;
directory. If using the Visual Studio IDE instead of the make-win.bat script, directories bin and
lib may need to be created manually to the SoundTouch
package root for the final executables. The make-win.bat script
creates these directories automatically.
</p>
<h3>2.2. Building in Gnu platforms</h3>
<p>The SoundTouch library can be compiled in
practically any platform supporting GNU compiler (GCC) tools.
SoundTouch have been tested with gcc version 3.3.4., but it
shouldn't be very specific about the gcc version. Assembler-level
performance optimizations for GNU platform are currently available in
x86 platforms only, they are automatically disabled and replaced with
standard C routines in other processor platforms.</p>
<p>To build and install the binaries, run the
following commands in the SoundTouch/ directory:</p>
<table border="0" cellpadding="0" cellspacing="4">
<tbody>
<tr valign="top">
<td>
<pre>./configure -</pre>
</td>
<td>
<p>Configures the SoundTouch package for the local
environment.</p>
</td>
</tr>
<tr valign="top">
<td>
<pre>make -</pre>
</td>
<td>
<p>Builds the SoundTouch library &amp;
SoundStretch utility.</p>
</td>
</tr>
<tr valign="top">
<td>
<pre>make install -</pre>
</td>
<td>
<p>Installs the SoundTouch &amp; BPM libraries
to <b>/usr/local/lib</b> and SoundStretch utility to <b>/usr/local/bin</b>.
Please notice that 'root' privileges may be required to install the
binaries to the destination locations.</p>
</td>
</tr>
</tbody>
</table>
<h4><b>2.2.1 Required GNU tools</b>&nbsp;</h4>
<p> Bash shell, GNU C++ compiler, libtool, autoconf and automake tools are required
for compiling
the SoundTouch library. These are usually included with the GNU/Linux distribution, but if
not, install these packages first. For example, in Ubuntu Linux these can be acquired and
installed with the following command:</p>
<pre><b>sudo apt-get install <font SIZE="2">automake autoconf libtool build-essential</font></b></pre>
<h4><b>2.2.2 Problems with GCC compiler compatibility</b></h4>
<p>At the release time the SoundTouch package has been tested to compile in
GNU/Linux platform. However, in past it's happened that new gcc versions aren't
necessarily compatible with the assembler settings used in the optimized
routines. <b>If you have problems getting the
SoundTouch library compiled, try the workaround of disabling the optimizations</b>
by editing the file &quot;include/STTypes.h&quot; and removing the following
definition there:</p>
<blockquote>
<pre>#define ALLOW_OPTIMIZATIONS 1</pre>
</blockquote>
<h4><b>2.2.3 Problems with configure script or build process</b>&nbsp;</h4>
<p>Incompatibilities between various GNU toolchain versions may cause errors when running the &quot;configure&quot; script or building the source
codes, if your GNU tool versions are not compatible with the versions used for
preparing the SoundTouch kit.&nbsp;</p>
<p>To resolve the issue, regenerate the configure scripts with your local tool
set by running
the &quot;<b>./bootstrap</b>&quot; script included in the SoundTouch source code
kit. After that, run the <b>configure</b> script and <b>make</b> as usually.</p>
<h4><b>2.2.4 Compiler issues with non-x86 processors</b></h4>
<p>SoundTouch library works also on non-x86 processors.</p>
<p>However, in case that you get compiler errors when trying to compile for non-Intel processor, edit the file
&quot;<b>source\SoundTouch\Makefile.am</b>&quot; and remove the &quot;<b>-msse2</b>&quot;
flag on the <b>AM_CXXFLAGS </b>line:</p>
<pre><b>AM_CXXFLAGS=-O3 -fcheck-new -I../../include&nbsp;&nbsp;&nbsp; # Note: -msse2 flag removed!</b></pre>
<p>After that, run &quot;<b>./bootstrap</b>&quot; script, and then run <b>configure</b>
and <b>make</b> again.</p>
<hr>
<h2>3. About implementation &amp; Usage tips</h2>
<h3>3.1. Supported sample data formats</h3>
<p>The sample data format can be chosen
between 16bit signed integer and 32bit floating point values, the
default is 32bit floating point. </p>
<p>
In Windows environment, the sample data format is chosen
in file &quot;STTypes.h&quot; by choosing one of the following
defines:</p>
<ul>
<li><span style="font-weight: bold;">#define INTEGER_SAMPLES</span>
for 16bit signed
integer</li>
<li><span style="font-weight: bold;">#define FLOAT_SAMPLES</span> for
32bit floating point</li>
</ul>
<p>
In GNU environment, the floating sample format is used by default, but
integer sample format can be chosen by giving the
following switch to the configure script:
<blockquote>
<pre>./configure --enable-integer-samples</pre>
</blockquote>
<p>The sample data can have either single (mono)
or double (stereo) audio channel. Stereo data is interleaved so
that every other data value is for left channel and every second
for right channel. Notice that while it'd be possible in theory
to process stereo sound as two separate mono channels, this isn't
recommended because processing the channels separately would
result in losing the phase coherency between the channels, which
consequently would ruin the stereo effect.</p>
<p>Sample rates between 8000-48000H are
supported.</p>
<h3>3.2. Processing latency</h3>
<p>The processing and latency constraints of
the SoundTouch library are:</p>
<ul>
<li>Input/output processing latency for the
SoundTouch processor is around 100 ms. This is when time-stretching is
used. If the rate transposing effect alone is used, the latency
requirement
is much shorter, see section 'About algorithms'.</li>
<li>Processing CD-quality sound (16bit stereo
sound with 44100H sample rate) in real-time or faster is possible
starting from processors equivalent to Intel Pentium 133Mh or better,
if using the "quick" processing algorithm. If not using the "quick"
mode or
if floating point sample data are being used, several times more CPU
power is typically required.</li>
</ul>
<h3>3.3. About algorithms</h3>
<p>SoundTouch provides three seemingly
independent effects: tempo, pitch and playback rate control.
These three controls are implemented as combination of two primary
effects, <em>sample rate transposing</em> and <em>time-stretching</em>.</p>
<p><em>Sample rate transposing</em> affects
both the audio stream duration and pitch. It's implemented simply
by converting the original audio sample stream to the&nbsp; desired
duration by interpolating from the original audio samples. In SoundTouch, linear interpolation with anti-alias filtering is
used. Theoretically a higher-order interpolation provide better
result than 1st order linear interpolation, but in audio
application linear interpolation together with anti-alias
filtering performs subjectively about as well as higher-order
filtering would.</p>
<p><em>Time-stretching </em>means changing
the audio stream duration without affecting it's pitch. SoundTouch
uses WSOLA-like time-stretching routines that operate in the time
domain. Compared to sample rate transposing, time-stretching is a
much heavier operation and also requires a longer processing
"window" of sound samples used by the
processing algorithm, thus increasing the algorithm input/output
latency. Typical i/o latency for the SoundTouch
time-stretch algorithm is around 100 ms.</p>
<p>Sample rate transposing and time-stretching
are then used together to produce the tempo, pitch and rate
controls:</p>
<ul>
<li><strong>'Tempo'</strong> control is
implemented purely by time-stretching.</li>
<li><strong>'Rate</strong>' control is implemented
purely by sample rate transposing.</li>
<li><strong>'Pitch</strong>' control is
implemented as a combination of time-stretching and sample rate
transposing. For example, to increase pitch the audio stream is first
time-stretched to longer duration (without affecting pitch) and then
transposed back to original duration by sample rate transposing, which
simultaneously reduces duration and increases pitch. The result is
original duration but increased pitch.</li>
</ul>
<h3>3.4 Tuning the algorithm parameters</h3>
<p>The time-stretch algorithm has few
parameters that can be tuned to optimize sound quality for
certain application. The current default parameters have been
chosen by iterative if-then analysis (read: "trial and error")
to obtain best subjective sound quality in pop/rock music
processing, but in applications processing different kind of
sound the default parameter set may result into a sub-optimal
result.</p>
<p>The time-stretch algorithm default
parameter values are set by the following #defines in file &quot;TDStretch.h&quot;:</p>
<blockquote>
<pre>#define DEFAULT_SEQUENCE_MS AUTOMATIC
#define DEFAULT_SEEKWINDOW_MS AUTOMATIC
#define DEFAULT_OVERLAP_MS 8</pre>
</blockquote>
<p>These parameters affect to the time-stretch
algorithm as follows:</p>
<ul>
<li><strong>DEFAULT_SEQUENCE_MS</strong>: This is
the default length of a single processing sequence in milliseconds
which determines the how the original sound is chopped in
the time-stretch algorithm. Larger values mean fewer sequences
are used in processing. In principle a larger value sounds better when
slowing down the tempo, but worse when increasing the tempo and vice
versa.&nbsp;<br>
<br>
By default, this setting value is calculated automatically according to
tempo value.<br>
</li>
<li><strong>DEFAULT_SEEKWINDOW_MS</strong>: The seeking window
default length in milliseconds is for the algorithm that seeks the best
possible overlapping location. This determines from how
wide a sample "window" the algorithm can use to find an optimal mixing
location when the sound sequences are to be linked back together.&nbsp;<br>
<br>
The bigger this window setting is, the higher the possibility to find a
better mixing position becomes, but at the same time large values may
cause a "drifting" sound artifact because neighboring sequences can be
chosen at more uneven intervals. If there's a disturbing artifact that
sounds as if a constant frequency was drifting around, try reducing
this setting.<br>
<br>
By default, this setting value is calculated automatically according to
tempo value.<br>
</li>
<li><strong>DEFAULT_OVERLAP_MS</strong>: Overlap
length in milliseconds. When the sound sequences are mixed back
together to form again a continuous sound stream, this parameter
defines how much the ends of the consecutive sequences will overlap with each other.<br>
<br>
This shouldn't be that critical parameter. If you reduce the
DEFAULT_SEQUENCE_MS setting by a large amount, you might wish to try a
smaller value on this.</li>
</ul>
<p>Notice that these parameters can also be
set during execution time with functions "<strong>TDStretch::setParameters()</strong>"
and "<strong>SoundTouch::setSetting()</strong>".</p>
<p>The table below summaries how the
parameters can be adjusted for different applications:</p>
<table border="1">
<tbody>
<tr>
<td valign="top"><strong>Parameter name</strong></td>
<td valign="top"><strong>Default value
magnitude</strong></td>
<td valign="top"><strong>Larger value
affects...</strong></td>
<td valign="top"><strong>Smaller value
affects...</strong></td>
<td valign="top"><strong>Effect to CPU burden</strong></td>
</tr>
<tr>
<td valign="top">
<pre>SEQUENCE_MS</pre>
</td>
<td valign="top">Default value is relatively
large, chosen for slowing down music tempo</td>
<td valign="top">Larger value is usually
better for slowing down tempo. Growing the value decelerates the
"echoing" artifact when slowing down the tempo.</td>
<td valign="top">Smaller value might be better
for speeding up tempo. Reducing the value accelerates the "echoing"
artifact when slowing down the tempo </td>
<td valign="top">Increasing the parameter
value reduces computation burden</td>
</tr>
<tr>
<td valign="top">
<pre>SEEKWINDOW_MS</pre>
</td>
<td valign="top">Default value is relatively
large, chosen for slowing down music tempo</td>
<td valign="top">Larger value eases finding a
good mixing position, but may cause a "drifting" artifact</td>
<td valign="top">Smaller reduce possibility to
find a good mixing position, but reduce the "drifting" artifact.</td>
<td valign="top">Increasing the parameter
value increases computation burden</td>
</tr>
<tr>
<td valign="top">
<pre>OVERLAP_MS</pre>
</td>
<td valign="top">Default value is relatively
large, chosen to suit with above parameters.</td>
<td valign="top">&nbsp;</td>
<td valign="top">If you reduce the "sequence
ms" setting, you might wish to try a smaller value.</td>
<td valign="top">Increasing the parameter
value increases computation burden</td>
</tr>
</tbody>
</table>
<h3>3.5 Performance Optimizations </h3>
<p><strong>General optimizations:</strong></p>
<p>The time-stretch routine has a 'quick' mode
that substantially speeds up the algorithm but may degrade the
sound quality by a small amount. This mode is activated by
calling SoundTouch::setSetting() function with parameter&nbsp; id
of SETTING_USE_QUICKSEEK and value "1", i.e. </p>
<blockquote>
<p>setSetting(SETTING_USE_QUICKSEEK, 1);</p>
</blockquote>
<p><strong>CPU-specific optimizations:</strong></p>
<ul>
<li>Intel MMX optimized routines are used with
compatible CPUs when 16bit integer sample type is used. MMX optimizations are available both in Win32 and Gnu/x86 platforms.
Compatible processors are Intel PentiumMMX and later; AMD K6-2, Athlon
and later. </li>
<li>Intel SSE optimized routines are used with
compatible CPUs when floating point sample type is used. SSE optimizations are currently implemented for Win32 platform only.
Processors compatible with SSE extension are Intel processors starting
from Pentium-III, and AMD processors starting from Athlon XP. </li>
<li>AMD 3DNow! optimized routines are used with
compatible CPUs when floating point sample type is used, but SSE
extension isn't supported . 3DNow! optimizations are currently
implemented for Win32 platform only. These optimizations are used in
AMD K6-2 and Athlon (classic) CPU's; better performing SSE routines are
used with AMD processor starting from Athlon XP. </li>
</ul>
<hr>
<h2><a name="SoundStretch"></a>4. SoundStretch audio processing utility
</h2>
<p>SoundStretch audio processing utility<br>
Copyright (c) Olli Parviainen 2002-2009</p>
<p>SoundStretch is a simple command-line
application that can change tempo, pitch and playback rates of
WAV sound files. This program is intended primarily to
demonstrate how the &quot;SoundTouch&quot; library can be used to
process sound in your own program, but it can as well be used for
processing sound files.</p>
<h3>4.1. SoundStretch Usage Instructions</h3>
<p>SoundStretch Usage syntax:</p>
<blockquote>
<pre>soundstretch infilename outfilename [switches]</pre>
</blockquote>
<p>Where: </p>
<table border="0" cellpadding="2" width="100%">
<tbody>
<tr>
<td valign="top">
<pre>&quot;infilename&quot;</pre>
</td>
<td valign="top">Name of the input sound
data file (in .WAV audio file format). Give &quot;stdin&quot; as filename to use
standard input pipe. </td>
</tr>
<tr>
<td valign="top">
<pre>&quot;outfilename&quot;</pre>
</td>
<td valign="top">Name of the output sound
file where the resulting sound is saved (in .WAV audio file format).
This parameter may be omitted if you&nbsp; don't want to save the
output
(e.g. when only calculating BPM rate with '-bpm' switch). Give &quot;stdout&quot;
as filename to use standard output pipe.</td>
</tr>
<tr>
<td valign="top">
<pre>&nbsp;[switches]</pre>
</td>
<td valign="top">Are one or more control
switches.</td>
</tr>
</tbody>
</table>
<p>Available control switches are:</p>
<table border="0" cellpadding="2" width="100%">
<tbody>
<tr>
<td valign="top">
<pre>-tempo=n </pre>
</td>
<td valign="top">Change the sound tempo by n
percents (n = -95.0 .. +5000.0 %) </td>
</tr>
<tr>
<td valign="top">
<pre>-pitch=n</pre>
</td>
<td valign="top">Change the sound pitch by n
semitones (n = -60.0 .. + 60.0 semitones) </td>
</tr>
<tr>
<td valign="top">
<pre>-rate=n</pre>
</td>
<td valign="top">Change the sound playback rate by
n percents (n = -95.0 .. +5000.0 %) </td>
</tr>
<tr>
<td valign="top">
<pre>-bpm=n</pre>
</td>
<td valign="top">Detect the Beats-Per-Minute (BPM) rate of the sound and adjust the tempo to meet 'n'
BPMs. When this switch is
applied, the &quot;-tempo&quot; switch is ignored. If "=n" is
omitted, i.e. switch &quot;-bpm&quot; is used alone, then the BPM rate is
estimated and displayed, but tempo not adjusted according to the BPM
value. </td>
</tr>
<tr>
<td valign="top">
<pre>-quick</pre>
</td>
<td valign="top">Use quicker tempo change
algorithm. Gains speed but loses sound quality. </td>
</tr>
<tr>
<td valign="top">
<pre>-naa</pre>
</td>
<td valign="top">Don't use anti-alias
filtering in sample rate transposing. Gains speed but loses sound
quality. </td>
</tr>
<tr>
<td valign="top">
<pre>-license</pre>
</td>
<td valign="top">Displays the program license
text (LGPL)</td>
</tr>
</tbody>
</table>
<p>Notes:</p>
<ul>
<li>To use standard input/output pipes for processing, give &quot;stdin&quot;
and &quot;stdout&quot; as input/output filenames correspondingly. The
standard input/output pipes will still carry the audio data in .wav audio
file format.</li>
<li>The numerical switches allow both integer (e.g. "-tempo=123") and decimal (e.g.
"-tempo=123.45") numbers.</li>
<li>The &quot;-naa&quot; and/or "-quick" switches can be
used to reduce CPU usage while compromising some sound quality </li>
<li>The BPM detection algorithm works by detecting
repeating bass or drum patterns at low frequencies of &lt;250Hz. A
lower-than-expected BPM figure may be reported for music with uneven or
complex bass patterns. </li>
</ul>
<h3>4.2. SoundStretch usage examples </h3>
<p><strong>Example 1</strong></p>
<p>The following command increases tempo of
the sound file &quot;originalfile.wav&quot; by 12.5% and stores result to file &quot;destinationfile.wav&quot;:</p>
<blockquote>
<pre>soundstretch originalfile.wav destinationfile.wav -tempo=12.5</pre>
</blockquote>
<p><strong>Example 2</strong></p>
<p>The following command decreases the sound
pitch (key) of the sound file &quot;orig.wav&quot; by two
semitones and stores the result to file &quot;dest.wav&quot;:</p>
<blockquote>
<pre>soundstretch orig.wav dest.wav -pitch=-2</pre>
</blockquote>
<p><strong>Example 3</strong></p>
<p>The following command processes the file &quot;orig.wav&quot; by decreasing the sound tempo by 25.3% and
increasing the sound pitch (key) by 1.5 semitones. Resulting .wav audio data is
directed to standard output pipe:</p>
<blockquote>
<pre>soundstretch orig.wav stdout -tempo=-25.3 -pitch=1.5</pre>
</blockquote>
<p><strong>Example 4</strong></p>
<p>The following command detects the BPM rate
of the file &quot;orig.wav&quot; and adjusts the tempo to match
100 beats per minute. Result is stored to file &quot;dest.wav&quot;:</p>
<blockquote>
<pre>soundstretch orig.wav dest.wav -bpm=100</pre>
</blockquote>
<p><strong>Example 5</strong></p>
<p>The following command reads .wav sound data from standard input pipe and
estimates the BPM rate:</p>
<blockquote>
<pre>soundstretch stdin -bpm</pre>
</blockquote>
<hr>
<h2>5. Change History</h2>
<h3>5.1. SoundTouch library Change History </h3>
<p><strong>1.5.0:</strong></p>
<ul>
<li>Added normalization to correlation calculation and improvement automatic seek/sequence parameter calculation to improve sound quality</li>
<li>Bugfixes:&nbsp;
<ul>
<li>Fixed negative array indexing in quick seek algorithm</li>
<li>FIR autoalias filter running too far in processing buffer</li>
<li>Check against zero sample count in rate transposing</li>
<li>Fix for x86-64 support: Removed pop/push instructions from the cpu detection algorithm.&nbsp;</li>
<li>Check against empty buffers in FIFOSampleBuffer</li>
<li>Other minor fixes &amp; code cleanup</li>
</ul>
</li>
<li>Fixes in compilation scripts for non-Intel platforms</li>
<li>Added Dynamic-Link-Library (DLL) version of SoundTouch library build,
provided with Delphi/Pascal wrapper for calling the dll routines</li>
<li>Added #define PREVENT_CLICK_AT_RATE_CROSSOVER that prevents a click artifact
when crossing the nominal pitch from either positive to negative side or vice
versa</li>
</ul>
<p><strong>1.4.1:</strong></p>
<ul>
<li>Fixed a buffer overflow bug in BPM detect algorithm routines if processing
more than 2048 samples at one call&nbsp;</li>
</ul>
<p><strong>1.4.0:</strong></p>
<ul>
<li>Improved sound quality by automatic calculation of time stretch algorithm
processing parameters according to tempo setting</li>
<li>Moved BPM detection routines from SoundStretch application into SoundTouch
library</li>
<li>Bugfixes: Usage of uninitialied variables, GNU build scripts, compiler errors
due to 'const' keyword mismatch.</li>
<li>Source code cleanup</li>
</ul>
<p><strong>v1.3.1:
</strong></p>
<ul>
<li>Changed static class declaration to GCC 4.x compiler compatible syntax.</li>
<li>Enabled MMX/SSE-optimized routines also for GCC compilers. Earlier
the MMX/SSE-optimized routines were written in compiler-specific inline
assembler, now these routines are migrated to use compiler intrinsic
syntax which allows compiling the same MMX/SSE-optimized source code with
both Visual C++ and GCC compilers. </li>
<li>Set floating point as the default sample format and added switch to
the GNU configure script for selecting the other sample format.</li>
</ul>
<p><strong>v1.3.0:
</strong></p>
<ul>
<li>Fixed tempo routine output duration inaccuracy due to rounding
error </li>
<li>Implemented separate processing routines for integer and
floating arithmetic to allow improvements to floating point routines
(earlier used algorithms mostly optimized for integer arithmetic also
for floating point samples) </li>
<li>Fixed a bug that distorts sound if sample rate changes during the
sound stream </li>
<li>Fixed a memory leak that appeared in MMX/SSE/3DNow! optimized
routines </li>
<li>Reduced redundant code pieces in MMX/SSE/3DNow! optimized
routines vs. the standard C routines.</li>
<li>MMX routine incompatibility with new gcc compiler versions </li>
<li>Other miscellaneous bug fixes </li>
</ul>
<p><strong>v1.2.1: </strong></p>
<ul>
<li>Added automake/autoconf scripts for GNU
platforms (in courtesy of David Durham)</li>
<li>Fixed SCALE overflow bug in rate transposer
routine.</li>
<li>Fixed 64bit address space bugs.</li>
<li>Created a 'soundtouch' namespace for
SAMPLETYPE definitions.</li>
</ul>
<p><strong>v1.2.0: </strong></p>
<ul>
<li>Added support for 32bit floating point sample
data type with SSE/3DNow! optimizations for Win32 platform (SSE/3DNow! optimizations currently not supported in GCC environment)</li>
<li>Replaced 'make-gcc' script for GNU environment
by master Makefile</li>
<li>Added time-stretch routine configurability to
SoundTouch main class</li>
<li>Bugfixes</li>
</ul>
<p><strong>v1.1.1: </strong></p>
<ul>
<li>Moved SoundTouch under lesser GPL license (LGPL). This allows using SoundTouch library in programs that aren't
released under GPL license. </li>
<li>Changed MMX routine organiation so that MMX optimized routines are now implemented in classes that are derived from
the basic classes having the standard non-mmx routines. </li>
<li>MMX routines to support gcc version 3. </li>
<li>Replaced windows makefiles by script using the .dsw files </li>
</ul>
<p><strong>v1.01: </strong></p>
<ul>
<li>&quot;mmx_gcc.cpp&quot;: Added "using namespace std" and
removed "return 0" from a function with void return value to fix
compiler errors when compiling the library in Solaris environment. </li>
<li>Moved file &quot;FIFOSampleBuffer.h&quot; to "include"
directory to allow accessing the FIFOSampleBuffer class from external
files. </li>
</ul>
<p><strong>v1.0: </strong></p>
<ul>
<li>Initial release </li>
</ul>
<p>&nbsp;</p>
<h3>5.2. SoundStretch application Change
History </h3>
<p><strong>1.4.0:</strong></p>
<ul>
<li>Moved BPM detection routines from SoundStretch application into SoundTouch
library</li>
<li>Allow using standard input/output pipes as audio processing input/output
streams</li>
</ul>
<p><strong>v1.3.0:</strong></p>
<ul>
<li>Simplified accessing WAV files with floating
point sample format.
</li>
</ul>
<p><strong>v1.2.1: </strong></p>
<ul>
<li>Fixed 64bit address space bugs.</li>
</ul>
<p><strong>v1.2.0: </strong></p>
<ul>
<li>Added support for 32bit floating point sample
data type</li>
<li>Restructured the BPM routines into separate
library</li>
<li>Fixed big-endian conversion bugs in WAV file
routines (hopefully :)</li>
</ul>
<p><strong>v1.1.1: </strong></p>
<ul>
<li>Fixed bugs in WAV file reading &amp; added
byte-order conversion for big-endian processors. </li>
<li>Moved SoundStretch source code under 'example'
directory to highlight difference from SoundTouch stuff. </li>
<li>Replaced windows makefiles by script using the .dsw files </li>
<li>Output file name isn't required if output
isn't desired (e.g. if using the switch '-bpm' in plain format only) </li>
</ul>
<p><strong>v1.1:</strong></p>
<ul>
<li>Fixed "Release" settings in Microsoft Visual
C++ project file (.dsp) </li>
<li>Added beats-per-minute (BPM) detection routine
and command-line switch &quot;-bpm&quot; </li>
</ul>
<p><strong>v1.01: </strong></p>
<ul>
<li>Initial release </li>
</ul>
<hr>
<h2 >6. Acknowledgements </h2>
<p >Kudos for these people who have contributed to development or submitted
bugfixes since
SoundTouch v1.3.1: </p>
<ul>
<li>Arthur A</li>
<li>Richard Ash</li>
<li>Stanislav Brabec</li>
<li>Christian Budde</li>
<li>Brian Cameron</li>
<li>Jason Champion</li>
<li>Patrick Colis</li>
<li>Justin Frankel</li>
<li>Jason Garland</li>
<li>Takashi Iwai</li>
<li>Paulo Pizarro</li>
<li>RJ Ryan</li>
<li>John Sheehy</li>
</ul>
<p >Moral greetings to all other contributors and users also!</p>
<hr>
<h2 >7. LICENSE </h2>
<p>SoundTouch audio processing library<br>
Copyright (c) Olli Parviainen</p>
<p>This library is free software; you can
redistribute it and/or modify it under the terms of the GNU
Lesser General Public License version 2.1 as published by the Free Software
Foundation.</p>
<p>This library is distributed in the hope
that it will be useful, but WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details.</p>
<p>You should have received a copy of the GNU
Lesser General Public 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>
<hr>
<!--
$Id: README.html 81 2009-12-28 20:51:18Z oparviai $
-->
</body>
</html>

View File

@ -10,10 +10,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/03/19 10:05:49 $
// File revision : $Revision: 1.13 $
// Last changed : $Date: 2009-10-31 16:37:24 +0200 (Sat, 31 Oct 2009) $
// File revision : $Revision: 4 $
//
// $Id: RateTransposer.cpp,v 1.13 2006/03/19 10:05:49 Olli Exp $
// $Id: RateTransposer.cpp 74 2009-10-31 14:37:24Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -42,10 +42,11 @@
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <stdexcept>
#include "RateTransposer.h"
#include "AAFilter.h"
using namespace std;
using namespace soundtouch;
@ -55,7 +56,7 @@ class RateTransposerInteger : public RateTransposer
{
protected:
int iSlopeCount;
uint uRate;
int iRate;
SAMPLETYPE sPrevSampleL, sPrevSampleR;
virtual void resetRegisters();
@ -84,7 +85,6 @@ class RateTransposerFloat : public RateTransposer
{
protected:
float fSlopeCount;
float fRateStep;
SAMPLETYPE sPrevSampleL, sPrevSampleR;
virtual void resetRegisters();
@ -103,18 +103,12 @@ public:
#ifndef min
#define min(a,b) ((a > b) ? b : a)
#define max(a,b) ((a < b) ? b : a)
#endif
// Operator 'new' is overloaded so that it automatically creates a suitable instance
// depending on if we've a MMX/SSE/etc-capable CPU available or not.
void * RateTransposer::operator new(size_t s)
{
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
assert(FALSE);
throw runtime_error("Error in RateTransoser::new: don't use \"new TDStretch\" directly, use \"newInstance\" to create a new instance instead!");
return NULL;
}
@ -132,8 +126,9 @@ RateTransposer *RateTransposer::newInstance()
// Constructor
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
{
uChannels = 2;
numChannels = 2;
bUseAAFilter = TRUE;
fRate = 0;
// Instantiates the anti-alias filter with default tap length
// of 32
@ -150,7 +145,7 @@ RateTransposer::~RateTransposer()
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
void RateTransposer::enableAAFilter(const BOOL newMode)
void RateTransposer::enableAAFilter(BOOL newMode)
{
bUseAAFilter = newMode;
}
@ -163,18 +158,18 @@ BOOL RateTransposer::isAAFilterEnabled() const
}
AAFilter *RateTransposer::getAAFilter() const
AAFilter *RateTransposer::getAAFilter()
{
return pAAFilter;
}
// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower
// uRate, larger faster uRates.
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
// iRate, larger faster iRates.
void RateTransposer::setRate(float newRate)
{
float fCutoff;
double fCutoff;
fRate = newRate;
@ -197,45 +192,47 @@ void RateTransposer::setRate(float newRate)
//
// It's allowed for 'output' and 'input' parameters to point to the same
// memory position.
/*
void RateTransposer::flushStoreBuffer()
{
if (storeBuffer.isEmpty()) return;
outputBuffer.moveSamples(storeBuffer);
}
*/
// Adds 'numSamples' pcs of samples from the 'samples' memory position into
// Adds 'nSamples' pcs of samples from the 'samples' memory position into
// the input of the object.
void RateTransposer::putSamples(const SAMPLETYPE *samples, uint numSamples)
void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
{
processSamples(samples, numSamples);
processSamples(samples, nSamples);
}
// Transposes up the sample rate, causing the observed playback 'rate' of the
// sound to decrease
void RateTransposer::upsample(const SAMPLETYPE *src, uint numSamples)
void RateTransposer::upsample(const SAMPLETYPE *src, uint nSamples)
{
int count, sizeTemp, num;
uint count, sizeTemp, num;
// If the parameter 'uRate' value is smaller than 'SCALE', first transpose
// the samples and then apply the anti-alias filter to remove aliasing.
// First check that there's enough room in 'storeBuffer'
// (+16 is to reserve some slack in the destination buffer)
sizeTemp = (int)((float)numSamples / fRate + 16.0f);
sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
// Transpose the samples, store the result into the end of "storeBuffer"
count = transpose(storeBuffer.ptrEnd(sizeTemp), src, numSamples);
count = transpose(storeBuffer.ptrEnd(sizeTemp), src, nSamples);
storeBuffer.putSamples(count);
// Apply the anti-alias filter to samples in "store output", output the
// result to "dest"
num = storeBuffer.numSamples();
count = pAAFilter->evaluate(outputBuffer.ptrEnd(num),
storeBuffer.ptrBegin(), num, uChannels);
storeBuffer.ptrBegin(), num, (uint)numChannels);
outputBuffer.putSamples(count);
// Remove the processed samples from "storeBuffer"
@ -245,16 +242,16 @@ void RateTransposer::upsample(const SAMPLETYPE *src, uint numSamples)
// Transposes down the sample rate, causing the observed playback 'rate' of the
// sound to increase
void RateTransposer::downsample(const SAMPLETYPE *src, uint numSamples)
void RateTransposer::downsample(const SAMPLETYPE *src, uint nSamples)
{
int count, sizeTemp;
uint count, sizeTemp;
// If the parameter 'uRate' value is larger than 'SCALE', first apply the
// anti-alias filter to remove high frequencies (prevent them from folding
// over the lover frequencies), then transpose. */
// over the lover frequencies), then transpose.
// Add the new samples to the end of the storeBuffer */
storeBuffer.putSamples(src, numSamples);
// Add the new samples to the end of the storeBuffer
storeBuffer.putSamples(src, nSamples);
// Anti-alias filter the samples to prevent folding and output the filtered
// data to tempBuffer. Note : because of the FIR filter length, the
@ -263,13 +260,15 @@ void RateTransposer::downsample(const SAMPLETYPE *src, uint numSamples)
sizeTemp = storeBuffer.numSamples();
count = pAAFilter->evaluate(tempBuffer.ptrEnd(sizeTemp),
storeBuffer.ptrBegin(), sizeTemp, uChannels);
storeBuffer.ptrBegin(), sizeTemp, (uint)numChannels);
if (count == 0) return;
// Remove the filtered samples from 'storeBuffer'
storeBuffer.receiveSamples(count);
// Transpose the samples (+16 is to reserve some slack in the destination buffer)
sizeTemp = (int)((float)numSamples / fRate + 16.0f);
sizeTemp = (uint)((float)nSamples / fRate + 16.0f);
count = transpose(outputBuffer.ptrEnd(sizeTemp), tempBuffer.ptrBegin(), count);
outputBuffer.putSamples(count);
}
@ -279,20 +278,20 @@ void RateTransposer::downsample(const SAMPLETYPE *src, uint numSamples)
// Returns amount of samples returned in the "dest" buffer.
// The maximum amount of samples that can be returned at a time is set by
// the 'set_returnBuffer_size' function.
void RateTransposer::processSamples(const SAMPLETYPE *src, uint numSamples)
void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
{
uint count;
uint sizeReq;
if (numSamples == 0) return;
if (nSamples == 0) return;
assert(pAAFilter);
// If anti-alias filter is turned off, simply transpose without applying
// the filter
if (bUseAAFilter == FALSE)
{
sizeReq = (int)((float)numSamples / fRate + 1.0f);
count = transpose(outputBuffer.ptrEnd(sizeReq), src, numSamples);
sizeReq = (uint)((float)nSamples / fRate + 1.0f);
count = transpose(outputBuffer.ptrEnd(sizeReq), src, nSamples);
outputBuffer.putSamples(count);
return;
}
@ -300,41 +299,42 @@ void RateTransposer::processSamples(const SAMPLETYPE *src, uint numSamples)
// Transpose with anti-alias filter
if (fRate < 1.0f)
{
upsample(src, numSamples);
upsample(src, nSamples);
}
else
{
downsample(src, numSamples);
downsample(src, nSamples);
}
}
// Transposes the sample rate of the given samples using linear interpolation.
// Returns the number of samples returned in the "dest" buffer
inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
inline uint RateTransposer::transpose(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
{
if (uChannels == 2)
if (numChannels == 2)
{
return transposeStereo(dest, src, numSamples);
return transposeStereo(dest, src, nSamples);
}
else
{
return transposeMono(dest, src, numSamples);
return transposeMono(dest, src, nSamples);
}
}
// Sets the number of channels, 1 = mono, 2 = stereo
void RateTransposer::setChannels(const uint numchannels)
void RateTransposer::setChannels(int nChannels)
{
if (uChannels == numchannels) return;
assert(nChannels > 0);
if (numChannels == nChannels) return;
assert(numchannels == 1 || numchannels == 2);
uChannels = numchannels;
assert(nChannels == 1 || nChannels == 2);
numChannels = nChannels;
storeBuffer.setChannels(uChannels);
tempBuffer.setChannels(uChannels);
outputBuffer.setChannels(uChannels);
storeBuffer.setChannels(numChannels);
tempBuffer.setChannels(numChannels);
outputBuffer.setChannels(numChannels);
// Inits the linear interpolation registers
resetRegisters();
@ -350,7 +350,7 @@ void RateTransposer::clear()
// Returns nonzero if there aren't any samples available for outputting.
uint RateTransposer::isEmpty()
int RateTransposer::isEmpty() const
{
int res;
@ -371,11 +371,10 @@ uint RateTransposer::isEmpty()
// Constructor
RateTransposerInteger::RateTransposerInteger() : RateTransposer()
{
// call these here as these are virtual functions; calling these
// from the base class constructor wouldn't execute the overloaded
// versions (<master yoda>peculiar C++ can be</my>).
resetRegisters();
setRate(1.0f);
// Notice: use local function calling syntax for sake of clarity,
// to indicate the fact that C++ constructor can't call virtual functions.
RateTransposerInteger::resetRegisters();
RateTransposerInteger::setRate(1.0f);
}
@ -396,12 +395,14 @@ void RateTransposerInteger::resetRegisters()
// Transposes the sample rate of the given samples using linear interpolation.
// 'Mono' version of the routine. Returns the number of samples returned in
// the "dest" buffer
uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
{
unsigned int i, used;
LONG_SAMPLETYPE temp, vol1;
used = 0;
if (nSamples == 0) return 0; // no samples, no work
used = 0;
i = 0;
// Process the last sample saved from the previous call first...
@ -411,7 +412,7 @@ uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *sr
temp = vol1 * sPrevSampleL + iSlopeCount * src[0];
dest[i] = (SAMPLETYPE)(temp / SCALE);
i++;
iSlopeCount += uRate;
iSlopeCount += iRate;
}
// now always (iSlopeCount > SCALE)
iSlopeCount -= SCALE;
@ -422,18 +423,18 @@ uint RateTransposerInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *sr
{
iSlopeCount -= SCALE;
used ++;
if (used >= numSamples - 1) goto end;
if (used >= nSamples - 1) goto end;
}
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
temp = src[used] * vol1 + iSlopeCount * src[used + 1];
dest[i] = (SAMPLETYPE)(temp / SCALE);
i++;
iSlopeCount += uRate;
iSlopeCount += iRate;
}
end:
// Store the last sample for the next round
sPrevSampleL = src[numSamples - 1];
sPrevSampleL = src[nSamples - 1];
return i;
}
@ -442,12 +443,12 @@ end:
// Transposes the sample rate of the given samples using linear interpolation.
// 'Stereo' version of the routine. Returns the number of samples returned in
// the "dest" buffer
uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
{
unsigned int srcPos, i, used;
LONG_SAMPLETYPE temp, vol1;
if (numSamples == 0) return 0; // no samples, no work
if (nSamples == 0) return 0; // no samples, no work
used = 0;
i = 0;
@ -461,7 +462,7 @@ uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *
temp = vol1 * sPrevSampleR + iSlopeCount * src[1];
dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
i++;
iSlopeCount += uRate;
iSlopeCount += iRate;
}
// now always (iSlopeCount > SCALE)
iSlopeCount -= SCALE;
@ -472,7 +473,7 @@ uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *
{
iSlopeCount -= SCALE;
used ++;
if (used >= numSamples - 1) goto end;
if (used >= nSamples - 1) goto end;
}
srcPos = 2 * used;
vol1 = (LONG_SAMPLETYPE)(SCALE - iSlopeCount);
@ -482,22 +483,22 @@ uint RateTransposerInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *
dest[2 * i + 1] = (SAMPLETYPE)(temp / SCALE);
i++;
iSlopeCount += uRate;
iSlopeCount += iRate;
}
end:
// Store the last sample for the next round
sPrevSampleL = src[2 * numSamples - 2];
sPrevSampleR = src[2 * numSamples - 1];
sPrevSampleL = src[2 * nSamples - 2];
sPrevSampleR = src[2 * nSamples - 1];
return i;
}
// Sets new target uRate. Normal uRate = 1.0, smaller values represent slower
// uRate, larger faster uRates.
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
// iRate, larger faster iRates.
void RateTransposerInteger::setRate(float newRate)
{
uRate = (int)(newRate * SCALE + 0.5f);
iRate = (int)(newRate * SCALE + 0.5f);
RateTransposer::setRate(newRate);
}
@ -511,11 +512,10 @@ void RateTransposerInteger::setRate(float newRate)
// Constructor
RateTransposerFloat::RateTransposerFloat() : RateTransposer()
{
// call these here as these are virtual functions; calling these
// from the base class constructor wouldn't execute the overloaded
// versions (<master yoda>peculiar C++ can be</my>).
resetRegisters();
setRate(1.0f);
// Notice: use local function calling syntax for sake of clarity,
// to indicate the fact that C++ constructor can't call virtual functions.
RateTransposerFloat::resetRegisters();
RateTransposerFloat::setRate(1.0f);
}
@ -536,7 +536,7 @@ void RateTransposerFloat::resetRegisters()
// Transposes the sample rate of the given samples using linear interpolation.
// 'Mono' version of the routine. Returns the number of samples returned in
// the "dest" buffer
uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
{
unsigned int i, used;
@ -552,23 +552,24 @@ uint RateTransposerFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src,
}
fSlopeCount -= 1.0f;
if (numSamples == 1) goto end;
while (1)
if (nSamples > 1)
{
while (fSlopeCount > 1.0f)
while (1)
{
fSlopeCount -= 1.0f;
used ++;
if (used >= numSamples - 1) goto end;
while (fSlopeCount > 1.0f)
{
fSlopeCount -= 1.0f;
used ++;
if (used >= nSamples - 1) goto end;
}
dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[used] + fSlopeCount * src[used + 1]);
i++;
fSlopeCount += fRate;
}
dest[i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[used] + fSlopeCount * src[used + 1]);
i++;
fSlopeCount += fRate;
}
end:
// Store the last sample for the next round
sPrevSampleL = src[numSamples - 1];
sPrevSampleL = src[nSamples - 1];
return i;
}
@ -577,11 +578,11 @@ end:
// Transposes the sample rate of the given samples using linear interpolation.
// 'Mono' version of the routine. Returns the number of samples returned in
// the "dest" buffer
uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples)
uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint nSamples)
{
unsigned int srcPos, i, used;
if (numSamples == 0) return 0; // no samples, no work
if (nSamples == 0) return 0; // no samples, no work
used = 0;
i = 0;
@ -597,30 +598,31 @@ uint RateTransposerFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *sr
// now always (iSlopeCount > 1.0f)
fSlopeCount -= 1.0f;
if (numSamples == 1) goto end;
while (1)
if (nSamples > 1)
{
while (fSlopeCount > 1.0f)
while (1)
{
fSlopeCount -= 1.0f;
used ++;
if (used >= numSamples - 1) goto end;
while (fSlopeCount > 1.0f)
{
fSlopeCount -= 1.0f;
used ++;
if (used >= nSamples - 1) goto end;
}
srcPos = 2 * used;
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos]
+ fSlopeCount * src[srcPos + 2]);
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1]
+ fSlopeCount * src[srcPos + 3]);
i++;
fSlopeCount += fRate;
}
srcPos = 2 * used;
dest[2 * i] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos]
+ fSlopeCount * src[srcPos + 2]);
dest[2 * i + 1] = (SAMPLETYPE)((1.0f - fSlopeCount) * src[srcPos + 1]
+ fSlopeCount * src[srcPos + 3]);
i++;
fSlopeCount += fRate;
}
end:
// Store the last sample for the next round
sPrevSampleL = src[2 * numSamples - 2];
sPrevSampleR = src[2 * numSamples - 1];
sPrevSampleL = src[2 * nSamples - 2];
sPrevSampleR = src[2 * nSamples - 1];
return i;
}

View File

@ -14,10 +14,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.10 $
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
// File revision : $Revision: 4 $
//
// $Id: RateTransposer.h,v 1.10 2006/02/05 16:44:06 Olli Exp $
// $Id: RateTransposer.h 63 2009-02-21 16:00:14Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -45,6 +45,7 @@
#ifndef RateTransposer_H
#define RateTransposer_H
#include <stddef.h>
#include "AAFilter.h"
#include "FIFOSamplePipe.h"
#include "FIFOSampleBuffer.h"
@ -68,7 +69,7 @@ protected:
float fRate;
uint uChannels;
int numChannels;
/// Buffer for collecting samples to feed the anti-alias filter between
/// two batches
@ -82,8 +83,6 @@ protected:
BOOL bUseAAFilter;
void init();
virtual void resetRegisters() = 0;
virtual uint transposeStereo(SAMPLETYPE *dest,
@ -92,12 +91,10 @@ protected:
virtual uint transposeMono(SAMPLETYPE *dest,
const SAMPLETYPE *src,
uint numSamples) = 0;
uint transpose(SAMPLETYPE *dest,
inline uint transpose(SAMPLETYPE *dest,
const SAMPLETYPE *src,
uint numSamples);
void flushStoreBuffer();
void downsample(const SAMPLETYPE *src,
uint numSamples);
void upsample(const SAMPLETYPE *src,
@ -117,7 +114,7 @@ public:
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
/// depending on if we're to use integer or floating point arithmetics.
void *operator new(size_t s);
static void *operator new(size_t s);
/// Use this function instead of "new" operator to create a new instance of this class.
/// This function automatically chooses a correct implementation, depending on if
@ -131,7 +128,7 @@ public:
FIFOSamplePipe *getStore() { return &storeBuffer; };
/// Return anti-alias filter object
AAFilter *getAAFilter() const;
AAFilter *getAAFilter();
/// Enables/disables the anti-alias filter. Zero to disable, nonzero to enable
void enableAAFilter(BOOL newMode);
@ -144,7 +141,7 @@ public:
virtual void setRate(float newRate);
/// Sets the number of channels, 1 = mono, 2 = stereo
void setChannels(uint channels);
void setChannels(int channels);
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into
/// the input of the object.
@ -154,7 +151,7 @@ public:
void clear();
/// Returns nonzero if there aren't any samples available for outputting.
uint isEmpty();
int isEmpty() const;
};
}

View File

@ -8,10 +8,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.16 $
// Last changed : $Date: 2009-05-17 14:30:57 +0300 (Sun, 17 May 2009) $
// File revision : $Revision: 3 $
//
// $Id: STTypes.h,v 1.16 2006/02/05 16:44:06 Olli Exp $
// $Id: STTypes.h 70 2009-05-17 11:30:57Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -39,63 +39,12 @@
#ifndef STTypes_H
#define STTypes_H
//#define INTEGER_SAMPLES 1
typedef unsigned int uint;
typedef unsigned long ulong;
#ifdef __x86_64__
typedef unsigned long long ulongptr;
#else
typedef unsigned long ulongptr;
#endif
#ifdef __GNUC__
// In GCC, include soundtouch_config.h made by config scritps
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `m' library (-lm). */
#define HAVE_LIBM 1
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
to 0 otherwise. */
#define HAVE_MALLOC 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Use Integer as Sample type */
//#define INTEGER_SAMPLES 1
/* Define as the return type of signal handlers (`int' or `void'). */
#define RETSIGTYPE void
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
#include "soundtouch_config.h"
#endif
#ifndef _WINDEF_
@ -103,19 +52,15 @@ typedef unsigned long ulongptr;
typedef int BOOL;
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#endif // _WINDEF_
namespace soundtouch
{
/// Activate these undef's to overrule the possible sampletype
/// setting inherited from some other header file:
//#undef INTEGER_SAMPLES
@ -142,22 +87,21 @@ namespace soundtouch
#endif
/// Define this to allow CPU-specific assembler optimizations. Notice that
/// having this enabled on non-x86 platforms doesn't matter; the compiler can
/// drop unsupported extensions on different platforms automatically.
/// However, if you're having difficulties getting the optimized routines
/// compiled with your compler (e.g. some gcc compiler versions may be picky),
/// you may wish to disable the optimizations to make the library compile.
#if !defined(_MSC_VER) || !defined(__x86_64__)
#define ALLOW_OPTIMIZATIONS 1
#define ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
#endif
#if (WIN32 || __i386__ || __x86_64__)
/// Define this to allow X86-specific assembler/intrinsic optimizations.
/// Notice that library contains also usual C++ versions of each of these
/// these routines, so if you're having difficulties getting the optimized
/// routines compiled for whatever reason, you may disable these optimizations
/// to make the library compile.
#define ALLOW_X86_OPTIMIZATIONS 1
#endif
// If defined, allows the SIMD-optimized routines to take minor shortcuts
// for improved performance. Undefine to require faithfully similar SIMD
// calculations as in normal C implementation.
#define ALLOW_NONEXACT_SIMD_OPTIMIZATION 1
#ifdef INTEGER_SAMPLES
@ -171,11 +115,9 @@ namespace soundtouch
#error "conflicting sample types defined"
#endif // FLOAT_SAMPLES
#ifdef ALLOW_OPTIMIZATIONS
#if (_WIN32 || __i386__ || __x86_64__)
// Allow MMX optimizations
#define ALLOW_MMX 1
#endif
#ifdef ALLOW_X86_OPTIMIZATIONS
// Allow MMX optimizations
#define ALLOW_MMX 1
#endif
#else
@ -185,18 +127,23 @@ namespace soundtouch
// data type for sample accumulation: Use double to utilize full precision.
typedef double LONG_SAMPLETYPE;
#ifdef ALLOW_OPTIMIZATIONS
#ifdef ALLOW_X86_OPTIMIZATIONS
// Allow 3DNow! and SSE optimizations
#if _WIN32
// #define ALLOW_3DNOW 1
#if WIN32
#define ALLOW_3DNOW 1
#endif
#if (_WIN32 || __i386__ || __x86_64__)
#define ALLOW_SSE 1
#endif
#define ALLOW_SSE 1
#endif
#endif // INTEGER_SAMPLES
};
#endif
// When this #define is active, eliminates a clicking sound when the "rate" or "pitch"
// parameter setting crosses from value <1 to >=1 or vice versa during processing.
// Default is off as such crossover is untypical case and involves a slight sound
// quality compromise.
//#define PREVENT_CLICK_AT_RATE_CROSSOVER 1
#endif

View File

@ -41,10 +41,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.13 $
// Last changed : $Date: 2009-05-19 07:57:30 +0300 (Tue, 19 May 2009) $
// File revision : $Revision: 4 $
//
// $Id: SoundTouch.cpp,v 1.13 2006/02/05 16:44:06 Olli Exp $
// $Id: SoundTouch.cpp 73 2009-05-19 04:57:30Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -82,8 +82,12 @@
#include "cpu_detect.h"
using namespace soundtouch;
/// test if two floating point numbers are equal
#define TEST_FLOAT_EQUAL(a, b) (fabs(a - b) < 1e-10)
/// Print library version string
/// Print library version string for autoconf
extern "C" void soundtouch_ac_test()
{
printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
@ -145,8 +149,8 @@ void SoundTouch::setChannels(uint numChannels)
throw std::runtime_error("Illegal number of channels");
}
channels = numChannels;
pRateTransposer->setChannels(numChannels);
pTDStretch->setChannels(numChannels);
pRateTransposer->setChannels((int)numChannels);
pTDStretch->setChannels((int)numChannels);
}
@ -236,10 +240,28 @@ void SoundTouch::calcEffectiveRateAndTempo()
tempo = virtualTempo / virtualPitch;
rate = virtualPitch * virtualRate;
if (rate != oldRate) pRateTransposer->setRate(rate);
if (tempo != oldTempo) pTDStretch->setTempo(tempo);
if (!TEST_FLOAT_EQUAL(rate,oldRate)) pRateTransposer->setRate(rate);
if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo);
if (rate > 1.0f)
#ifndef PREVENT_CLICK_AT_RATE_CROSSOVER
if (rate <= 1.0f)
{
if (output != pTDStretch)
{
FIFOSamplePipe *tempoOut;
assert(output == pRateTransposer);
// move samples in the current output buffer to the output of pTDStretch
tempoOut = pTDStretch->getOutput();
tempoOut->moveSamples(*output);
// move samples in pitch transposer's store buffer to tempo changer's input
pTDStretch->moveSamples(*pRateTransposer->getStore());
output = pTDStretch;
}
}
else
#endif
{
if (output != pRateTransposer)
{
@ -255,23 +277,6 @@ void SoundTouch::calcEffectiveRateAndTempo()
output = pRateTransposer;
}
}
else
{
if (output != pTDStretch)
{
FIFOSamplePipe *tempoOut;
assert(output == pRateTransposer);
// move samples in the current output buffer to the output of pTDStretch
tempoOut = pTDStretch->getOutput();
tempoOut->moveSamples(*output);
// move samples in pitch transposer's store buffer to tempo changer's input
pTDStretch->moveSamples(*pRateTransposer->getStore());
output = pTDStretch;
}
}
}
@ -280,13 +285,13 @@ void SoundTouch::setSampleRate(uint srate)
{
bSrateSet = TRUE;
// set sample rate, leave other tempo changer parameters as they are.
pTDStretch->setParameters(srate);
pTDStretch->setParameters((int)srate);
}
// Adds 'numSamples' pcs of samples from the 'samples' memory position into
// the input of the object.
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint numSamples)
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
{
if (bSrateSet == FALSE)
{
@ -309,22 +314,23 @@ void SoundTouch::putSamples(const SAMPLETYPE *samples, uint numSamples)
// (may happen if 'rate' changes from a non-zero value to zero)
pTDStretch->moveSamples(*pRateTransposer);
}
pTDStretch->putSamples(samples, numSamples);
pTDStretch->putSamples(samples, nSamples);
}
*/
#ifndef PREVENT_CLICK_AT_RATE_CROSSOVER
else if (rate <= 1.0f)
{
// transpose the rate down, output the transposed sound to tempo changer buffer
assert(output == pTDStretch);
pRateTransposer->putSamples(samples, numSamples);
pRateTransposer->putSamples(samples, nSamples);
pTDStretch->moveSamples(*pRateTransposer);
}
else
#endif
{
assert(rate > 1.0f);
// evaluate the tempo changer, then transpose the rate up,
assert(output == pRateTransposer);
pTDStretch->putSamples(samples, numSamples);
pTDStretch->putSamples(samples, nSamples);
pRateTransposer->moveSamples(*pTDStretch);
}
}
@ -366,9 +372,9 @@ void SoundTouch::flush()
// Changes a setting controlling the processing system behaviour. See the
// 'SETTING_...' defines for available setting ID's.
BOOL SoundTouch::setSetting(uint settingId, uint value)
BOOL SoundTouch::setSetting(int settingId, int value)
{
uint sampleRate, sequenceMs, seekWindowMs, overlapMs;
int sampleRate, sequenceMs, seekWindowMs, overlapMs;
// read current tdstretch routine parameters
pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
@ -415,20 +421,20 @@ BOOL SoundTouch::setSetting(uint settingId, uint value)
// 'SETTING_...' defines for available setting ID's.
//
// Returns the setting value.
uint SoundTouch::getSetting(uint settingId) const
int SoundTouch::getSetting(int settingId) const
{
uint temp;
int temp;
switch (settingId)
{
case SETTING_USE_AA_FILTER :
return pRateTransposer->isAAFilterEnabled();
return (uint)pRateTransposer->isAAFilterEnabled();
case SETTING_AA_FILTER_LENGTH :
return pRateTransposer->getAAFilter()->getLength();
case SETTING_USE_QUICKSEEK :
return pTDStretch->isQuickSeekEnabled();
return (uint) pTDStretch->isQuickSeekEnabled();
case SETTING_SEQUENCE_MS:
pTDStretch->getParameters(NULL, &temp, NULL, NULL);

View File

@ -41,10 +41,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.14 $
// Last changed : $Date: 2009-12-28 22:10:14 +0200 (Mon, 28 Dec 2009) $
// File revision : $Revision: 4 $
//
// $Id: SoundTouch.h,v 1.14 2006/02/05 16:44:06 Olli Exp $
// $Id: SoundTouch.h 78 2009-12-28 20:10:14Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -79,10 +79,10 @@ namespace soundtouch
{
/// Soundtouch library version string
#define SOUNDTOUCH_VERSION "1.3.1"
#define SOUNDTOUCH_VERSION "1.5.0"
/// SoundTouch library version id
#define SOUNDTOUCH_VERSION_ID 010301
#define SOUNDTOUCH_VERSION_ID (10500)
//
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
@ -223,16 +223,16 @@ public:
/// 'SETTING_...' defines for available setting ID's.
///
/// \return 'TRUE' if the setting was succesfully changed
BOOL setSetting(uint settingId, ///< Setting ID number. see SETTING_... defines.
uint value ///< New setting value.
BOOL setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
int value ///< New setting value.
);
/// Reads a setting controlling the processing system behaviour. See the
/// 'SETTING_...' defines for available setting ID's.
///
/// \return the setting value.
uint getSetting(uint settingId ///< Setting ID number, see SETTING_... defines.
) const;
int getSetting(int settingId ///< Setting ID number, see SETTING_... defines.
) const;
/// Returns number of samples currently unprocessed.
virtual uint numUnprocessedSamples() const;

View File

@ -13,10 +13,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.24 $
// Last changed : $Date: 2009-12-28 21:27:04 +0200 (Mon, 28 Dec 2009) $
// File revision : $Revision: 1.12 $
//
// $Id: TDStretch.cpp,v 1.24 2006/02/05 16:44:06 Olli Exp $
// $Id: TDStretch.cpp 77 2009-12-28 19:27:04Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -42,23 +42,21 @@
////////////////////////////////////////////////////////////////////////////////
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <limits.h>
#include <math.h>
#include <assert.h>
#include <math.h>
#include <float.h>
#include <stdexcept>
#include "STTypes.h"
#include "cpu_detect.h"
#include "TDStretch.h"
#include <stdio.h>
using namespace soundtouch;
#ifndef min
#define min(a,b) ((a > b) ? b : a)
#define max(a,b) ((a < b) ? b : a)
#endif
#define max(x, y) (((x) > (y)) ? (x) : (y))
/*****************************************************************************
@ -67,17 +65,18 @@ using namespace soundtouch;
*
*****************************************************************************/
// Table for the hierarchical mixing position seeking algorithm
int scanOffsets[4][24]={
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
static const short _scanOffsets[5][24]={
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{ 121, 114, 97, 114, 98, 105, 108, 32, 104, 99, 117, 111,
116, 100, 110, 117, 111, 115, 0, 0, 0, 0, 0, 0}};
/*****************************************************************************
*
@ -88,19 +87,25 @@ int scanOffsets[4][24]={
TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
{
bQuickseek = FALSE;
bQuickSeek = FALSE;
channels = 2;
bMidBufferDirty = FALSE;
pMidBuffer = NULL;
pRefMidBufferUnaligned = NULL;
overlapLength = 0;
bAutoSeqSetting = TRUE;
bAutoSeekSetting = TRUE;
// outDebt = 0;
skipFract = 0;
tempo = 1.0f;
setParameters(48000, DEFAULT_SEQUENCE_MS, DEFAULT_SEEKWINDOW_MS, DEFAULT_OVERLAP_MS);
setTempo(1.0f);
}
clear();
}
@ -111,14 +116,6 @@ TDStretch::~TDStretch()
}
// Calculates the x having the closest 2^x value for the given value
static int _getClosest2Power(double value)
{
return (int)(log(value) / log(2.0) + 0.5);
}
// Sets routine control parameters. These control are certain time constants
// defining how the sound is stretched to the desired duration.
@ -129,18 +126,36 @@ static int _getClosest2Power(double value)
// position (default = 28 ms)
// 'overlapMS' = overlapping length (default = 12 ms)
void TDStretch::setParameters(uint aSampleRate, uint aSequenceMS,
uint aSeekWindowMS, uint aOverlapMS)
void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
int aSeekWindowMS, int aOverlapMS)
{
this->sampleRate = aSampleRate;
this->sequenceMs = aSequenceMS;
this->seekWindowMs = aSeekWindowMS;
this->overlapMs = aOverlapMS;
// accept only positive parameter values - if zero or negative, use old values instead
if (aSampleRate > 0) this->sampleRate = aSampleRate;
if (aOverlapMS > 0) this->overlapMs = aOverlapMS;
seekLength = (sampleRate * seekWindowMs) / 1000;
seekWindowLength = (sampleRate * sequenceMs) / 1000;
if (aSequenceMS > 0)
{
this->sequenceMs = aSequenceMS;
bAutoSeqSetting = FALSE;
}
else if (aSequenceMS == 0)
{
// if zero, use automatic setting
bAutoSeqSetting = TRUE;
}
maxOffset = seekLength;
if (aSeekWindowMS > 0)
{
this->seekWindowMs = aSeekWindowMS;
bAutoSeekSetting = FALSE;
}
else if (aSeekWindowMS == 0)
{
// if zero, use automatic setting
bAutoSeekSetting = TRUE;
}
calcSeqParameters();
calculateOverlapLength(overlapMs);
@ -154,7 +169,7 @@ void TDStretch::setParameters(uint aSampleRate, uint aSequenceMS,
/// Get routine control parameters, see setParameters() function.
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
/// value isn't returned.
void TDStretch::getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekWindowMs, uint *pOverlapMs)
void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const
{
if (pSampleRate)
{
@ -163,12 +178,12 @@ void TDStretch::getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekW
if (pSequenceMs)
{
*pSequenceMs = sequenceMs;
*pSequenceMs = (bAutoSeqSetting) ? (USE_AUTO_SEQUENCE_LEN) : sequenceMs;
}
if (pSeekWindowMs)
{
*pSeekWindowMs = seekWindowMs;
*pSeekWindowMs = (bAutoSeekSetting) ? (USE_AUTO_SEEKWINDOW_LEN) : seekWindowMs;
}
if (pOverlapMs)
@ -178,15 +193,15 @@ void TDStretch::getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekW
}
// Overlaps samples in 'midBuffer' with the samples in 'input'
void TDStretch::overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const
// Overlaps samples in 'midBuffer' with the samples in 'pInput'
void TDStretch::overlapMono(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput) const
{
int i, itemp;
for (i = 0; i < (int)overlapLength ; i ++)
for (i = 0; i < overlapLength ; i ++)
{
itemp = overlapLength - i;
output[i] = (input[i] * i + pMidBuffer[i] * itemp ) / overlapLength; // >> overlapDividerBits;
pOutput[i] = (pInput[i] * i + pMidBuffer[i] * itemp ) / overlapLength; // >> overlapDividerBits;
}
}
@ -194,11 +209,7 @@ void TDStretch::overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const
void TDStretch::clearMidBuffer()
{
if (bMidBufferDirty)
{
memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength);
bMidBufferDirty = FALSE;
}
memset(pMidBuffer, 0, 2 * sizeof(SAMPLETYPE) * overlapLength);
}
@ -213,8 +224,7 @@ void TDStretch::clearInput()
void TDStretch::clear()
{
outputBuffer.clear();
inputBuffer.clear();
clearMidBuffer();
clearInput();
}
@ -223,24 +233,24 @@ void TDStretch::clear()
// to enable
void TDStretch::enableQuickSeek(BOOL enable)
{
bQuickseek = enable;
bQuickSeek = enable;
}
// Returns nonzero if the quick seeking algorithm is enabled.
BOOL TDStretch::isQuickSeekEnabled() const
{
return bQuickseek;
return bQuickSeek;
}
// Seeks for the optimal overlap-mixing position.
uint TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
int TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
{
if (channels == 2)
{
// stereo sound
if (bQuickseek)
if (bQuickSeek)
{
return seekBestOverlapPositionStereoQuick(refPos);
}
@ -252,7 +262,7 @@ uint TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
else
{
// mono sound
if (bQuickseek)
if (bQuickSeek)
{
return seekBestOverlapPositionMonoQuick(refPos);
}
@ -266,17 +276,17 @@ uint TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
// Overlaps samples in 'midBuffer' with the samples in 'inputBuffer' at position
// Overlaps samples in 'midBuffer' with the samples in 'pInputBuffer' at position
// of 'ovlPos'.
inline void TDStretch::overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint ovlPos) const
inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, uint ovlPos) const
{
if (channels == 2)
{
// stereo sound
overlapStereo(output, input + 2 * ovlPos);
overlapStereo(pOutput, pInput + 2 * ovlPos);
} else {
// mono sound.
overlapMono(output, input + ovlPos);
overlapMono(pOutput, pInput + ovlPos);
}
}
@ -289,16 +299,16 @@ inline void TDStretch::overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint
// The best position is determined as the position where the two overlapped
// sample sequences are 'most alike', in terms of the highest cross-correlation
// value over the overlapping period
uint TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
int TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
{
uint bestOffs;
LONG_SAMPLETYPE bestCorr, corr;
uint i;
int bestOffs;
double bestCorr, corr;
int i;
// Slopes the amplitudes of the 'midBuffer' samples
precalcCorrReferenceStereo();
bestCorr = INT_MIN;
bestCorr = FLT_MIN;
bestOffs = 0;
// Scans for the best correlation value by testing each possible position
@ -307,7 +317,10 @@ uint TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
{
// Calculates correlation value for the mixing position corresponding
// to 'i'
corr = calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer);
corr = (double)calcCrossCorrStereo(refPos + 2 * i, pRefMidBuffer);
// heuristic rule to slightly favour values close to mid of the range
double tmp = (double)(2 * i - seekLength) / (double)seekLength;
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
// Checks for the highest correlation value
if (corr > bestCorr)
@ -329,18 +342,18 @@ uint TDStretch::seekBestOverlapPositionStereo(const SAMPLETYPE *refPos)
// The best position is determined as the position where the two overlapped
// sample sequences are 'most alike', in terms of the highest cross-correlation
// value over the overlapping period
uint TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
int TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
{
uint j;
uint bestOffs;
LONG_SAMPLETYPE bestCorr, corr;
uint scanCount, corrOffset, tempOffset;
int j;
int bestOffs;
double bestCorr, corr;
int scanCount, corrOffset, tempOffset;
// Slopes the amplitude of the 'midBuffer' samples
precalcCorrReferenceStereo();
bestCorr = INT_MIN;
bestOffs = 0;
bestCorr = FLT_MIN;
bestOffs = _scanOffsets[0][0];
corrOffset = 0;
tempOffset = 0;
@ -353,14 +366,17 @@ uint TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
for (scanCount = 0;scanCount < 4; scanCount ++)
{
j = 0;
while (scanOffsets[scanCount][j])
while (_scanOffsets[scanCount][j])
{
tempOffset = corrOffset + scanOffsets[scanCount][j];
tempOffset = corrOffset + _scanOffsets[scanCount][j];
if (tempOffset >= seekLength) break;
// Calculates correlation value for the mixing position corresponding
// to 'tempOffset'
corr = calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer);
corr = (double)calcCrossCorrStereo(refPos + 2 * tempOffset, pRefMidBuffer);
// heuristic rule to slightly favour values close to mid of the range
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
// Checks for the highest correlation value
if (corr > bestCorr)
@ -386,17 +402,17 @@ uint TDStretch::seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos)
// The best position is determined as the position where the two overlapped
// sample sequences are 'most alike', in terms of the highest cross-correlation
// value over the overlapping period
uint TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos)
int TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos)
{
uint bestOffs;
LONG_SAMPLETYPE bestCorr, corr;
uint tempOffset;
int bestOffs;
double bestCorr, corr;
int tempOffset;
const SAMPLETYPE *compare;
// Slopes the amplitude of the 'midBuffer' samples
precalcCorrReferenceMono();
bestCorr = INT_MIN;
bestCorr = FLT_MIN;
bestOffs = 0;
// Scans for the best correlation value by testing each possible position
@ -407,7 +423,10 @@ uint TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos)
// Calculates correlation value for the mixing position corresponding
// to 'tempOffset'
corr = calcCrossCorrMono(pRefMidBuffer, compare);
corr = (double)calcCrossCorrMono(pRefMidBuffer, compare);
// heuristic rule to slightly favour values close to mid of the range
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
// Checks for the highest correlation value
if (corr > bestCorr)
@ -429,18 +448,18 @@ uint TDStretch::seekBestOverlapPositionMono(const SAMPLETYPE *refPos)
// The best position is determined as the position where the two overlapped
// sample sequences are 'most alike', in terms of the highest cross-correlation
// value over the overlapping period
uint TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos)
int TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos)
{
uint j;
uint bestOffs;
LONG_SAMPLETYPE bestCorr, corr;
uint scanCount, corrOffset, tempOffset;
int j;
int bestOffs;
double bestCorr, corr;
int scanCount, corrOffset, tempOffset;
// Slopes the amplitude of the 'midBuffer' samples
precalcCorrReferenceMono();
bestCorr = INT_MIN;
bestOffs = 0;
bestCorr = FLT_MIN;
bestOffs = _scanOffsets[0][0];
corrOffset = 0;
tempOffset = 0;
@ -453,14 +472,17 @@ uint TDStretch::seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos)
for (scanCount = 0;scanCount < 4; scanCount ++)
{
j = 0;
while (scanOffsets[scanCount][j])
while (_scanOffsets[scanCount][j])
{
tempOffset = corrOffset + scanOffsets[scanCount][j];
tempOffset = corrOffset + _scanOffsets[scanCount][j];
if (tempOffset >= seekLength) break;
// Calculates correlation value for the mixing position corresponding
// to 'tempOffset'
corr = calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer);
corr = (double)calcCrossCorrMono(refPos + tempOffset, pRefMidBuffer);
// heuristic rule to slightly favour values close to mid of the range
double tmp = (double)(2 * tempOffset - seekLength) / seekLength;
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
// Checks for the highest correlation value
if (corr > bestCorr)
@ -486,29 +508,82 @@ void TDStretch::clearCrossCorrState()
}
/// Calculates processing sequence length according to tempo setting
void TDStretch::calcSeqParameters()
{
// Adjust tempo param according to tempo, so that variating processing sequence length is used
// at varius tempo settings, between the given low...top limits
#define AUTOSEQ_TEMPO_LOW 0.5 // auto setting low tempo range (-50%)
#define AUTOSEQ_TEMPO_TOP 2.0 // auto setting top tempo range (+100%)
// sequence-ms setting values at above low & top tempo
#define AUTOSEQ_AT_MIN 125.0
#define AUTOSEQ_AT_MAX 50.0
#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))
// seek-window-ms setting values at above low & top tempo
#define AUTOSEEK_AT_MIN 25.0
#define AUTOSEEK_AT_MAX 15.0
#define AUTOSEEK_K ((AUTOSEEK_AT_MAX - AUTOSEEK_AT_MIN) / (AUTOSEQ_TEMPO_TOP - AUTOSEQ_TEMPO_LOW))
#define AUTOSEEK_C (AUTOSEEK_AT_MIN - (AUTOSEEK_K) * (AUTOSEQ_TEMPO_LOW))
#define CHECK_LIMITS(x, mi, ma) (((x) < (mi)) ? (mi) : (((x) > (ma)) ? (ma) : (x)))
double seq, seek;
if (bAutoSeqSetting)
{
seq = AUTOSEQ_C + AUTOSEQ_K * tempo;
seq = CHECK_LIMITS(seq, AUTOSEQ_AT_MAX, AUTOSEQ_AT_MIN);
sequenceMs = (int)(seq + 0.5);
}
if (bAutoSeekSetting)
{
seek = AUTOSEEK_C + AUTOSEEK_K * tempo;
seek = CHECK_LIMITS(seek, AUTOSEEK_AT_MAX, AUTOSEEK_AT_MIN);
seekWindowMs = (int)(seek + 0.5);
}
// Update seek window lengths
seekWindowLength = (sampleRate * sequenceMs) / 1000;
if (seekWindowLength < 2 * overlapLength)
{
seekWindowLength = 2 * overlapLength;
}
seekLength = (sampleRate * seekWindowMs) / 1000;
}
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
// tempo, larger faster tempo.
void TDStretch::setTempo(float newTempo)
{
uint intskip;
int intskip;
tempo = newTempo;
// Calculate new sequence duration
calcSeqParameters();
// Calculate ideal skip length (according to tempo value)
nominalSkip = tempo * (seekWindowLength - overlapLength);
skipFract = 0;
intskip = (int)(nominalSkip + 0.5f);
// Calculate how many samples are needed in the 'inputBuffer' to
// process another batch of samples
sampleReq = max(intskip + overlapLength, seekWindowLength) + maxOffset;
//sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength / 2;
sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength;
}
// Sets the number of channels, 1 = mono, 2 = stereo
void TDStretch::setChannels(uint numChannels)
void TDStretch::setChannels(int numChannels)
{
assert(numChannels > 0);
if (channels == numChannels) return;
assert(numChannels == 1 || numChannels == 2);
@ -520,6 +595,7 @@ void TDStretch::setChannels(uint numChannels)
// nominal tempo, no need for processing, just pass the samples through
// to outputBuffer
/*
void TDStretch::processNominalTempo()
{
assert(tempo == 1.0f);
@ -547,13 +623,15 @@ void TDStretch::processNominalTempo()
// Simply bypass samples from input to output
outputBuffer.moveSamples(inputBuffer);
}
*/
#include <stdio.h>
// Processes as many processing frames of the samples 'inputBuffer', store
// the result into 'outputBuffer'
void TDStretch::processSamples()
{
uint ovlSkip, offset;
int ovlSkip, offset;
int temp;
/* Removed this small optimization - can introduce a click to sound when tempo setting
@ -566,23 +644,10 @@ void TDStretch::processSamples()
}
*/
if (bMidBufferDirty == FALSE)
{
// if midBuffer is empty, move the first samples of the input stream
// into it
if (inputBuffer.numSamples() < overlapLength)
{
// wait until we've got overlapLength samples
return;
}
memcpy(pMidBuffer, inputBuffer.ptrBegin(), channels * overlapLength * sizeof(SAMPLETYPE));
inputBuffer.receiveSamples(overlapLength);
bMidBufferDirty = TRUE;
}
// Process samples as long as there are enough samples in 'inputBuffer'
// to form a processing frame.
while (inputBuffer.numSamples() >= sampleReq)
// while ((int)inputBuffer.numSamples() >= sampleReq - (outDebt / 4))
while ((int)inputBuffer.numSamples() >= sampleReq)
{
// If tempo differs from the normal ('SCALE'), scan for the best overlapping
// position
@ -592,23 +657,36 @@ void TDStretch::processSamples()
// samples in 'midBuffer' using sliding overlapping
// ... first partially overlap with the end of the previous sequence
// (that's in 'midBuffer')
overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), offset);
outputBuffer.putSamples(overlapLength);
overlap(outputBuffer.ptrEnd((uint)overlapLength), inputBuffer.ptrBegin(), (uint)offset);
outputBuffer.putSamples((uint)overlapLength);
// ... then copy sequence samples from 'inputBuffer' to output
temp = (seekWindowLength - 2 * overlapLength);// & 0xfffffffe;
if (temp > 0)
// ... then copy sequence samples from 'inputBuffer' to output:
temp = (seekLength / 2 - offset);
// compensate cumulated output length diff vs. ideal output
// temp -= outDebt / 4;
// update ideal vs. true output difference
// outDebt += temp;
// length of sequence
// temp += (seekWindowLength - 2 * overlapLength);
temp = (seekWindowLength - 2 * overlapLength);
// crosscheck that we don't have buffer overflow...
if ((int)inputBuffer.numSamples() < (offset + temp + overlapLength * 2))
{
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), temp);
continue; // just in case, shouldn't really happen
}
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * (offset + overlapLength), (uint)temp);
// Copies the end of the current sequence from 'inputBuffer' to
// 'midBuffer' for being mixed with the beginning of the next
// processing sequence and so on
assert(offset + seekWindowLength <= inputBuffer.numSamples());
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + seekWindowLength - overlapLength),
assert((offset + temp + overlapLength * 2) <= (int)inputBuffer.numSamples());
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp + overlapLength),
channels * sizeof(SAMPLETYPE) * overlapLength);
bMidBufferDirty = TRUE;
// Remove the processed samples from the input buffer. Update
// the difference between integer & nominal skip step to 'skipFract'
@ -616,17 +694,17 @@ void TDStretch::processSamples()
skipFract += nominalSkip; // real skip size
ovlSkip = (int)skipFract; // rounded to integer skip
skipFract -= ovlSkip; // maintain the fraction part, i.e. real vs. integer skip
inputBuffer.receiveSamples(ovlSkip);
inputBuffer.receiveSamples((uint)ovlSkip);
}
}
// Adds 'numsamples' pcs of samples from the 'samples' memory position into
// the input of the object.
void TDStretch::putSamples(const SAMPLETYPE *samples, uint numSamples)
void TDStretch::putSamples(const SAMPLETYPE *samples, uint nSamples)
{
// Add the samples into the input buffer
inputBuffer.putSamples(samples, numSamples);
inputBuffer.putSamples(samples, nSamples);
// Process the samples in input buffer
processSamples();
}
@ -634,10 +712,11 @@ void TDStretch::putSamples(const SAMPLETYPE *samples, uint numSamples)
/// Set new overlap length parameter & reallocate RefMidBuffer if necessary.
void TDStretch::acceptNewOverlapLength(uint newOverlapLength)
void TDStretch::acceptNewOverlapLength(int newOverlapLength)
{
uint prevOvl;
int prevOvl;
assert(newOverlapLength >= 0);
prevOvl = overlapLength;
overlapLength = newOverlapLength;
@ -647,12 +726,11 @@ void TDStretch::acceptNewOverlapLength(uint newOverlapLength)
delete[] pRefMidBufferUnaligned;
pMidBuffer = new SAMPLETYPE[overlapLength * 2];
bMidBufferDirty = TRUE;
clearMidBuffer();
pRefMidBufferUnaligned = new SAMPLETYPE[2 * overlapLength + 16 / sizeof(SAMPLETYPE)];
// ensure that 'pRefMidBuffer' is aligned to 16 byte boundary for efficiency
pRefMidBuffer = (SAMPLETYPE *)((((ulongptr)pRefMidBufferUnaligned) + 15) & -16);
pRefMidBuffer = (SAMPLETYPE *)((((ulong)pRefMidBufferUnaligned) + 15) & (ulong)-16);
}
}
@ -662,18 +740,16 @@ void TDStretch::acceptNewOverlapLength(uint newOverlapLength)
void * TDStretch::operator new(size_t s)
{
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
assert(FALSE);
throw std::runtime_error("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
return NULL;
}
TDStretch * TDStretch::newInstance()
{
uint uExtensions = 0;
uint uExtensions;
#if !defined(_MSC_VER) || !defined(__x86_64__)
uExtensions = detectCPUextensions();
#endif
// Check if MMX/SSE/3DNow! instruction set extensions supported by CPU
@ -760,33 +836,44 @@ void TDStretch::precalcCorrReferenceMono()
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
// version of the routine.
void TDStretch::overlapStereo(short *output, const short *input) const
void TDStretch::overlapStereo(short *poutput, const short *input) const
{
int i;
short temp;
uint cnt2;
int cnt2;
for (i = 0; i < (int)overlapLength ; i ++)
for (i = 0; i < overlapLength ; i ++)
{
temp = (short)(overlapLength - i);
cnt2 = 2 * i;
output[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength;
output[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength;
poutput[cnt2] = (input[cnt2] * i + pMidBuffer[cnt2] * temp ) / overlapLength;
poutput[cnt2 + 1] = (input[cnt2 + 1] * i + pMidBuffer[cnt2 + 1] * temp ) / overlapLength;
}
}
// Calculates the x having the closest 2^x value for the given value
static int _getClosest2Power(double value)
{
return (int)(log(value) / log(2.0) + 0.5);
}
/// Calculates overlap period length in samples.
/// Integer version rounds overlap length to closest power of 2
/// for a divide scaling operation.
void TDStretch::calculateOverlapLength(uint overlapMs)
void TDStretch::calculateOverlapLength(int aoverlapMs)
{
uint newOvl;
int newOvl;
overlapDividerBits = _getClosest2Power((sampleRate * overlapMs) / 1000.0);
assert(aoverlapMs >= 0);
// 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
// the extra most significatnt bit left unused in result by signed multiplication
overlapDividerBits = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
if (overlapDividerBits > 9) overlapDividerBits = 9;
if (overlapDividerBits < 4) overlapDividerBits = 4;
newOvl = 1<<overlapDividerBits;
if (overlapDividerBits < 3) overlapDividerBits = 3;
newOvl = (int)pow(2.0, (int)overlapDividerBits + 1); // +1 => account for -1 above
acceptNewOverlapLength(newOvl);
@ -800,31 +887,41 @@ void TDStretch::calculateOverlapLength(uint overlapMs)
long TDStretch::calcCrossCorrMono(const short *mixingPos, const short *compare) const
{
long corr;
uint i;
long norm;
int i;
corr = 0;
corr = norm = 0;
for (i = 1; i < overlapLength; i ++)
{
corr += (mixingPos[i] * compare[i]) >> overlapDividerBits;
norm += (mixingPos[i] * mixingPos[i]) >> overlapDividerBits;
}
return corr;
// Normalize result by dividing by sqrt(norm) - this step is easiest
// done using floating point operation
if (norm == 0) norm = 1; // to avoid div by zero
return (long)((double)corr * SHRT_MAX / sqrt((double)norm));
}
long TDStretch::calcCrossCorrStereo(const short *mixingPos, const short *compare) const
{
long corr;
uint i;
long norm;
int i;
corr = 0;
corr = norm = 0;
for (i = 2; i < 2 * overlapLength; i += 2)
{
corr += (mixingPos[i] * compare[i] +
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBits;
norm += (mixingPos[i] * mixingPos[i] + mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBits;
}
return corr;
// Normalize result by dividing by sqrt(norm) - this step is easiest
// done using floating point operation
if (norm == 0) norm = 1; // to avoid div by zero
return (long)((double)corr * SHRT_MAX / sqrt((double)norm));
}
#endif // INTEGER_SAMPLES
@ -869,11 +966,11 @@ void TDStretch::precalcCorrReferenceMono()
}
// SSE-optimized version of the function overlapStereo
void TDStretch::overlapStereo(float *output, const float *input) const
// Overlaps samples in 'midBuffer' with the samples in 'pInput'
void TDStretch::overlapStereo(float *pOutput, const float *pInput) const
{
int i;
uint cnt2;
int cnt2;
float fTemp;
float fScale;
float fi;
@ -885,18 +982,19 @@ void TDStretch::overlapStereo(float *output, const float *input) const
fTemp = (float)(overlapLength - i) * fScale;
fi = (float)i * fScale;
cnt2 = 2 * i;
output[cnt2 + 0] = input[cnt2 + 0] * fi + pMidBuffer[cnt2 + 0] * fTemp;
output[cnt2 + 1] = input[cnt2 + 1] * fi + pMidBuffer[cnt2 + 1] * fTemp;
pOutput[cnt2 + 0] = pInput[cnt2 + 0] * fi + pMidBuffer[cnt2 + 0] * fTemp;
pOutput[cnt2 + 1] = pInput[cnt2 + 1] * fi + pMidBuffer[cnt2 + 1] * fTemp;
}
}
/// Calculates overlap period length in samples.
void TDStretch::calculateOverlapLength(uint overlapMs)
/// Calculates overlapInMsec period length in samples.
void TDStretch::calculateOverlapLength(int overlapInMsec)
{
uint newOvl;
int newOvl;
newOvl = (sampleRate * overlapMs) / 1000;
assert(overlapInMsec >= 0);
newOvl = (sampleRate * overlapInMsec) / 1000;
if (newOvl < 16) newOvl = 16;
// must be divisible by 8
@ -910,31 +1008,38 @@ void TDStretch::calculateOverlapLength(uint overlapMs)
double TDStretch::calcCrossCorrMono(const float *mixingPos, const float *compare) const
{
double corr;
uint i;
double norm;
int i;
corr = 0;
corr = norm = 0;
for (i = 1; i < overlapLength; i ++)
{
corr += mixingPos[i] * compare[i];
norm += mixingPos[i] * mixingPos[i];
}
return corr;
if (norm < 1e-9) norm = 1.0; // to avoid div by zero
return corr / sqrt(norm);
}
double TDStretch::calcCrossCorrStereo(const float *mixingPos, const float *compare) const
{
double corr;
uint i;
double norm;
int i;
corr = 0;
corr = norm = 0;
for (i = 2; i < 2 * overlapLength; i += 2)
{
corr += mixingPos[i] * compare[i] +
mixingPos[i + 1] * compare[i + 1];
norm += mixingPos[i] * mixingPos[i] +
mixingPos[i + 1] * mixingPos[i + 1];
}
return corr;
if (norm < 1e-9) norm = 1.0; // to avoid div by zero
return corr / sqrt(norm);
}
#endif // FLOAT_SAMPLES

View File

@ -4,8 +4,8 @@
/// while maintaining the original pitch by using a time domain WSOLA-like method
/// with several performance-increasing tweaks.
///
/// Note : MMX optimized functions reside in a separate, platform-specific file,
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
/// Note : MMX/SSE optimized functions reside in separate, platform-specific files
/// 'mmx_optimized.cpp' and 'sse_optimized.cpp'
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
@ -13,10 +13,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.16 $
// Last changed : $Date: 2009-05-17 14:35:13 +0300 (Sun, 17 May 2009) $
// File revision : $Revision: 4 $
//
// $Id: TDStretch.h,v 1.16 2006/02/05 16:44:06 Olli Exp $
// $Id: TDStretch.h 71 2009-05-17 11:35:13Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -44,6 +44,7 @@
#ifndef TDStretch_H
#define TDStretch_H
#include <stddef.h>
#include "STTypes.h"
#include "RateTransposer.h"
#include "FIFOSamplePipe.h"
@ -51,7 +52,13 @@
namespace soundtouch
{
// Default values for sound processing parameters:
/// Default values for sound processing parameters:
/// Notice that the default parameters are tuned for contemporary popular music
/// processing. For speech processing applications these parameters suit better:
/// #define DEFAULT_SEQUENCE_MS 40
/// #define DEFAULT_SEEKWINDOW_MS 15
/// #define DEFAULT_OVERLAP_MS 8
///
/// Default length of a single processing sequence, in milliseconds. This determines to how
/// long sequences the original sound is chopped in the time-stretch algorithm.
@ -61,11 +68,41 @@ namespace soundtouch
/// and vice versa.
///
/// Increasing this value reduces computational burden & vice versa.
#define DEFAULT_SEQUENCE_MS 63
//#define DEFAULT_SEQUENCE_MS 40
#define DEFAULT_SEQUENCE_MS USE_AUTO_SEQUENCE_LEN
#define DEFAULT_SEEKWINDOW_MS 17
/// Giving this value for the sequence length sets automatic parameter value
/// according to tempo setting (recommended)
#define USE_AUTO_SEQUENCE_LEN 0
#define DEFAULT_OVERLAP_MS 7
/// Seeking window default length in milliseconds for algorithm that finds the best possible
/// overlapping location. This determines from how wide window the algorithm may look for an
/// optimal joining location when mixing the sound sequences back together.
///
/// The bigger this window setting is, the higher the possibility to find a better mixing
/// position will become, but at the same time large values may cause a "drifting" artifact
/// because consequent sequences will be taken at more uneven intervals.
///
/// If there's a disturbing artifact that sounds as if a constant frequency was drifting
/// around, try reducing this setting.
///
/// Increasing this value increases computational burden & vice versa.
//#define DEFAULT_SEEKWINDOW_MS 15
#define DEFAULT_SEEKWINDOW_MS USE_AUTO_SEEKWINDOW_LEN
/// Giving this value for the seek window length sets automatic parameter value
/// according to tempo setting (recommended)
#define USE_AUTO_SEEKWINDOW_LEN 0
/// Overlap length in milliseconds. When the chopped sound sequences are mixed back together,
/// to form a continuous sound stream, this parameter defines over how long period the two
/// consecutive sequences are let to overlap each other.
///
/// This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting
/// by a large amount, you might wish to try a smaller value on this.
///
/// Increasing this value increases computational burden & vice versa.
#define DEFAULT_OVERLAP_MS 8
/// Class that does the time-stretch (tempo change) effect for the processed
@ -73,44 +110,46 @@ namespace soundtouch
class TDStretch : public FIFOProcessor
{
protected:
uint channels;
uint sampleReq;
int channels;
int sampleReq;
float tempo;
SAMPLETYPE *pMidBuffer;
SAMPLETYPE *pRefMidBuffer;
SAMPLETYPE *pRefMidBufferUnaligned;
uint overlapLength;
uint overlapDividerBits;
uint slopingDivider;
uint seekLength;
uint seekWindowLength;
uint maxOffset;
int overlapLength;
int seekLength;
int seekWindowLength;
int overlapDividerBits;
int slopingDivider;
float nominalSkip;
float skipFract;
FIFOSampleBuffer outputBuffer;
FIFOSampleBuffer inputBuffer;
BOOL bQuickseek;
BOOL bMidBufferDirty;
BOOL bQuickSeek;
// int outDebt;
// BOOL bMidBufferDirty;
uint sampleRate;
uint sequenceMs;
uint seekWindowMs;
uint overlapMs;
int sampleRate;
int sequenceMs;
int seekWindowMs;
int overlapMs;
BOOL bAutoSeqSetting;
BOOL bAutoSeekSetting;
void acceptNewOverlapLength(uint newOverlapLength);
void acceptNewOverlapLength(int newOverlapLength);
virtual void clearCrossCorrState();
void calculateOverlapLength(uint overlapMs);
void calculateOverlapLength(int overlapMs);
virtual LONG_SAMPLETYPE calcCrossCorrStereo(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
virtual LONG_SAMPLETYPE calcCrossCorrMono(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
virtual uint seekBestOverlapPositionStereo(const SAMPLETYPE *refPos);
virtual uint seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos);
virtual uint seekBestOverlapPositionMono(const SAMPLETYPE *refPos);
virtual uint seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos);
uint seekBestOverlapPosition(const SAMPLETYPE *refPos);
virtual int seekBestOverlapPositionStereo(const SAMPLETYPE *refPos);
virtual int seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos);
virtual int seekBestOverlapPositionMono(const SAMPLETYPE *refPos);
virtual int seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos);
int seekBestOverlapPosition(const SAMPLETYPE *refPos);
virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;
@ -121,7 +160,7 @@ protected:
void precalcCorrReferenceMono();
void precalcCorrReferenceStereo();
void processNominalTempo();
void calcSeqParameters();
/// Changes the tempo of the given sound samples.
/// Returns amount of samples returned in the "output" buffer.
@ -135,7 +174,7 @@ public:
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
/// depending on if we've a MMX/SSE/etc-capable CPU available or not.
void *operator new(size_t s);
static void *operator new(size_t s);
/// Use this function instead of "new" operator to create a new instance of this class.
/// This function automatically chooses a correct feature set depending on if the CPU
@ -159,7 +198,7 @@ public:
void clearInput();
/// Sets the number of channels, 1 = mono, 2 = stereo
void setChannels(uint numChannels);
void setChannels(int numChannels);
/// Enables/disables the quick position seeking algorithm. Zero to disable,
/// nonzero to enable
@ -176,16 +215,16 @@ public:
/// 'seekwindowMS' = seeking window length for scanning the best overlapping
/// position
/// 'overlapMS' = overlapping length
void setParameters(uint sampleRate, ///< Samplerate of sound being processed (Hz)
uint sequenceMS = DEFAULT_SEQUENCE_MS, ///< Single processing sequence length (ms)
uint seekwindowMS = DEFAULT_SEEKWINDOW_MS, ///< Offset seeking window length (ms)
uint overlapMS = DEFAULT_OVERLAP_MS ///< Sequence overlapping length (ms)
void setParameters(int sampleRate, ///< Samplerate of sound being processed (Hz)
int sequenceMS = -1, ///< Single processing sequence length (ms)
int seekwindowMS = -1, ///< Offset seeking window length (ms)
int overlapMS = -1 ///< Sequence overlapping length (ms)
);
/// Get routine control parameters, see setParameters() function.
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
/// value isn't returned.
void getParameters(uint *pSampleRate, uint *pSequenceMs, uint *pSeekWindowMs, uint *pOverlapMs);
void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const;
/// Adds 'numsamples' pcs of samples from the 'samples' memory position into
/// the input of the object.
@ -200,26 +239,26 @@ public:
// Implementation-specific class declarations:
//#ifdef ALLOW_MMX
// /// Class that implements MMX optimized routines for 16bit integer samples type.
// class TDStretchMMX : public TDStretch
// {
// protected:
// long calcCrossCorrStereo(const short *mixingPos, const short *compare) const;
// virtual void overlapStereo(short *output, const short *input) const;
// virtual void clearCrossCorrState();
// };
//#endif /// ALLOW_MMX
//
//
//#ifdef ALLOW_3DNOW
// /// Class that implements 3DNow! optimized routines for floating point samples type.
// class TDStretch3DNow : public TDStretch
// {
// protected:
// double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
// };
//#endif /// ALLOW_3DNOW
#ifdef ALLOW_MMX
/// Class that implements MMX optimized routines for 16bit integer samples type.
class TDStretchMMX : public TDStretch
{
protected:
long calcCrossCorrStereo(const short *mixingPos, const short *compare) const;
virtual void overlapStereo(short *output, const short *input) const;
virtual void clearCrossCorrState();
};
#endif /// ALLOW_MMX
#ifdef ALLOW_3DNOW
/// Class that implements 3DNow! optimized routines for floating point samples type.
class TDStretch3DNow : public TDStretch
{
protected:
double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
};
#endif /// ALLOW_3DNOW
#ifdef ALLOW_SSE

View File

@ -17,10 +17,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.15 $
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
// File revision : $Revision: 4 $
//
// $Id: WavFile.cpp,v 1.15 2006/02/05 16:44:06 Olli Exp $
// $Id: WavFile.cpp 63 2009-02-21 16:00:14Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -48,20 +48,18 @@
#include <stdio.h>
#include <stdexcept>
#include <string>
#include <cstring>
#include <assert.h>
#include <limits.h>
#include <cstdlib>
#include <cstring>
#include "WavFile.h"
using namespace std;
const static char riffStr[] = "RIFF";
const static char waveStr[] = "WAVE";
const static char fmtStr[] = "fmt ";
const static char dataStr[] = "data";
static const char riffStr[] = "RIFF";
static const char waveStr[] = "WAVE";
static const char fmtStr[] = "fmt ";
static const char dataStr[] = "data";
//////////////////////////////////////////////////////////////////////////////
@ -142,8 +140,6 @@ const static char dataStr[] = "data";
WavInFile::WavInFile(const char *fileName)
{
int hdrsOk;
// Try to open the file for reading
fptr = fopen(fileName, "rb");
if (fptr == NULL)
@ -155,22 +151,45 @@ WavInFile::WavInFile(const char *fileName)
throw runtime_error(msg);
}
init();
}
WavInFile::WavInFile(FILE *file)
{
// Try to open the file for reading
fptr = file;
if (!file)
{
// didn't succeed
string msg = "Error : Unable to access input stream for reading";
throw runtime_error(msg);
}
init();
}
/// Init the WAV file stream
void WavInFile::init()
{
int hdrsOk;
// assume file stream is already open
assert(fptr);
// Read the file headers
hdrsOk = readWavHeaders();
if (hdrsOk != 0)
{
// Something didn't match in the wav file headers
string msg = "File \"";
msg += fileName;
msg += "\" is corrupt or not a WAV file";
string msg = "Input file is corrupt or not a WAV file";
throw runtime_error(msg);
}
if (header.format.fixed != 1)
{
string msg = "File \"";
msg += fileName;
msg += "\" uses unsupported encoding.";
string msg = "Input file uses unsupported encoding.";
throw runtime_error(msg);
}
@ -181,7 +200,8 @@ WavInFile::WavInFile(const char *fileName)
WavInFile::~WavInFile()
{
close();
if (fptr) fclose(fptr);
fptr = NULL;
}
@ -197,7 +217,7 @@ void WavInFile::rewind()
}
int WavInFile::checkCharTags()
int WavInFile::checkCharTags() const
{
// header.format.fmt should equal to 'fmt '
if (memcmp(fmtStr, header.format.fmt, 4) != 0) return -1;
@ -225,10 +245,11 @@ int WavInFile::read(char *buffer, int maxElems)
if (afterDataRead > header.data.data_len)
{
// Don't read more samples than are marked available in header
numBytes = header.data.data_len - dataRead;
numBytes = (int)header.data.data_len - (int)dataRead;
assert(numBytes >= 0);
}
assert(buffer);
numBytes = fread(buffer, 1, numBytes, fptr);
dataRead += numBytes;
@ -242,6 +263,7 @@ int WavInFile::read(short *buffer, int maxElems)
int numBytes;
int numElems;
assert(buffer);
if (header.format.bits_per_sample == 8)
{
// 8 bit format
@ -267,7 +289,7 @@ int WavInFile::read(short *buffer, int maxElems)
if (afterDataRead > header.data.data_len)
{
// Don't read more samples than are marked available in header
numBytes = header.data.data_len - dataRead;
numBytes = (int)header.data.data_len - (int)dataRead;
assert(numBytes >= 0);
}
@ -313,13 +335,6 @@ int WavInFile::eof() const
}
void WavInFile::close()
{
fclose(fptr);
fptr = NULL;
}
// test if character code is between a white space ' ' and little 'z'
static int isAlpha(char c)
@ -329,9 +344,9 @@ static int isAlpha(char c)
// test if all characters are between a white space ' ' and little 'z'
static int isAlphaStr(char *str)
static int isAlphaStr(const char *str)
{
int c;
char c;
c = str[0];
while (c)
@ -347,7 +362,7 @@ static int isAlphaStr(char *str)
int WavInFile::readRIFFBlock()
{
fread(&(header.riff), sizeof(WavRiff), 1, fptr);
if (fread(&(header.riff), sizeof(WavRiff), 1, fptr) != 1) return -1;
// swap 32bit data byte order if necessary
_swap32((unsigned int &)header.riff.package_len);
@ -369,7 +384,7 @@ int WavInFile::readHeaderBlock()
string sLabel;
// lead label string
fread(label, 1, 4, fptr);
if (fread(label, 1, 4, fptr) !=4) return -1;
label[4] = 0;
if (isAlphaStr(label) == 0) return -1; // not a valid label
@ -383,13 +398,13 @@ int WavInFile::readHeaderBlock()
memcpy(header.format.fmt, fmtStr, 4);
// read length of the format field
fread(&nLen, sizeof(int), 1, fptr);
if (fread(&nLen, sizeof(int), 1, fptr) != 1) return -1;
// swap byte order if necessary
_swap32((unsigned int &)nLen); // int format_len;
header.format.format_len = nLen;
// calculate how much length differs from expected
nDump = nLen - (sizeof(header.format) - 8);
nDump = nLen - ((int)sizeof(header.format) - 8);
// if format_len is larger than expected, read only as much data as we've space for
if (nDump > 0)
@ -398,7 +413,7 @@ int WavInFile::readHeaderBlock()
}
// read data
fread(&(header.format.fixed), nLen, 1, fptr);
if (fread(&(header.format.fixed), nLen, 1, fptr) != 1) return -1;
// swap byte order if necessary
_swap16((unsigned short &)header.format.fixed); // short int fixed;
@ -420,7 +435,7 @@ int WavInFile::readHeaderBlock()
{
// 'data' block
memcpy(header.data.data_field, dataStr, 4);
fread(&(header.data.data_len), sizeof(uint), 1, fptr);
if (fread(&(header.data.data_len), sizeof(uint), 1, fptr) != 1) return -1;
// swap byte order if necessary
_swap32((unsigned int &)header.data.data_len);
@ -434,11 +449,11 @@ int WavInFile::readHeaderBlock()
// unknown block
// read length
fread(&len, sizeof(len), 1, fptr);
if (fread(&len, sizeof(len), 1, fptr) != 1) return -1;
// scan through the block
for (i = 0; i < len; i ++)
{
fread(&temp, 1, 1, fptr);
if (fread(&temp, 1, 1, fptr) != 1) return -1;
if (feof(fptr)) return -1; // unexpected eof
}
}
@ -499,7 +514,8 @@ uint WavInFile::getDataSizeInBytes() const
uint WavInFile::getNumSamples() const
{
return header.data.data_len / header.format.byte_per_sample;
if (header.format.byte_per_sample == 0) return 0;
return header.data.data_len / (unsigned short)header.format.byte_per_sample;
}
@ -536,15 +552,30 @@ WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int chann
fillInHeader(sampleRate, bits, channels);
writeHeader();
flushTime = flushRate;
}
WavOutFile::WavOutFile(FILE *file, int sampleRate, int bits, int channels)
{
bytesWritten = 0;
fptr = file;
if (fptr == NULL)
{
string msg = "Error : Unable to access output file stream.";
throw runtime_error(msg);
}
fillInHeader(sampleRate, bits, channels);
writeHeader();
}
WavOutFile::~WavOutFile()
{
close();
finishHeader();
if (fptr) fclose(fptr);
fptr = NULL;
}
@ -569,11 +600,11 @@ void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels)
header.format.format_len = 0x10;
header.format.fixed = 1;
header.format.channel_number = (short)channels;
header.format.sample_rate = sampleRate;
header.format.sample_rate = (int)sampleRate;
header.format.bits_per_sample = (short)bits;
header.format.byte_per_sample = (short)(bits * channels / 8);
header.format.byte_rate = header.format.byte_per_sample * sampleRate;
header.format.sample_rate = sampleRate;
header.format.byte_rate = header.format.byte_per_sample * (int)sampleRate;
header.format.sample_rate = (int)sampleRate;
// fill in the 'data' part..
@ -598,6 +629,7 @@ void WavOutFile::finishHeader()
void WavOutFile::writeHeader()
{
WavHeader hdrTemp;
int res;
// swap byte order if necessary
hdrTemp = header;
@ -613,30 +645,18 @@ void WavOutFile::writeHeader()
// write the supplemented header in the beginning of the file
fseek(fptr, 0, SEEK_SET);
fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr);
res = fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr);
if (res != 1)
{
throw runtime_error("Error while writing to a wav file.");
}
// jump back to the end of the file
fseek(fptr, 0, SEEK_END);
}
void WavOutFile::close()
{
finishHeader();
fclose(fptr);
fptr = NULL;
}
void WavOutFile::flush( int numElems )
{
flushTime -= numElems;
if( flushTime < 0 )
{
flushTime += flushRate;
finishHeader();
}
}
void WavOutFile::write(const char *buffer, int numElems)
{
int res;
@ -654,7 +674,6 @@ void WavOutFile::write(const char *buffer, int numElems)
}
bytesWritten += numElems;
flush( numElems );
}
@ -698,7 +717,6 @@ void WavOutFile::write(const short *buffer, int numElems)
throw runtime_error("Error while writing to a wav file.");
}
bytesWritten += 2 * numElems;
flush( numElems*2 );
}
}
@ -722,7 +740,6 @@ void WavOutFile::write(const float *buffer, int numElems)
}
write(temp, numElems);
flush( numElems );
delete[] temp;
}

View File

@ -16,10 +16,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.7 $
// Last changed : $Date: 2009-02-21 18:00:14 +0200 (Sat, 21 Feb 2009) $
// File revision : $Revision: 4 $
//
// $Id: WavFile.h,v 1.7 2006/02/05 16:44:06 Olli Exp $
// $Id: WavFile.h 63 2009-02-21 16:00:14Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -105,13 +105,16 @@ private:
/// WAV header information
WavHeader header;
/// Init the WAV file stream
void init();
/// Read WAV file headers.
/// \return zero if all ok, nonzero if file format is invalid.
int readWavHeaders();
/// Checks WAV file header tags.
/// \return zero if all ok, nonzero if file format is invalid.
int checkCharTags();
int checkCharTags() const;
/// Reads a single WAV file header block.
/// \return zero if all ok, nonzero if file format is invalid.
@ -125,13 +128,11 @@ public:
/// throws 'runtime_error' exception.
WavInFile(const char *filename);
WavInFile(FILE *file);
/// Destructor: Closes the file.
~WavInFile();
/// Close the file. Notice that file is automatically closed also when the
/// class instance is deleted.
void close();
/// Rewind to beginning of the file
void rewind();
@ -203,9 +204,6 @@ private:
/// Counter of how many bytes have been written to the file so far.
int bytesWritten;
/// number of bytes to be written before next flush.
int flushTime;
/// Fills in WAV file header information.
void fillInHeader(const uint sampleRate, const uint bits, const uint channels);
@ -216,14 +214,6 @@ private:
/// Writes the WAV file header.
void writeHeader();
/// Flushes the WAV file every so often -- writes header info for the current
/// data length and then returns the seek position to the end of the WAV for
/// continued writing. This method is called from each write() method.
void flush( int numElems );
/// Flush the WAVheader every 32kb written
static const int flushRate = 0x8000;
public:
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
/// if file creation fails.
@ -233,6 +223,8 @@ public:
int channels ///< Number of channels (1=mono, 2=stereo)
);
WavOutFile(FILE *file, int sampleRate, int bits, int channels);
/// Destructor: Finalizes & closes the WAV file.
~WavOutFile();
@ -253,12 +245,6 @@ public:
void write(const float *buffer, ///< Pointer to sample data buffer.
int numElems ///< How many array items are to be written to file.
);
/// Finalize & close the WAV file. Automatically supplements the WAV file header
/// information according to written data etc.
///
/// Notice that file is automatically closed also when the class instance is deleted.
void close();
};
#endif

View File

@ -12,10 +12,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.4 $
// Last changed : $Date: 2008-02-10 18:26:55 +0200 (Sun, 10 Feb 2008) $
// File revision : $Revision: 4 $
//
// $Id: cpu_detect.h,v 1.4 2006/02/05 16:44:06 Olli Exp $
// $Id: cpu_detect.h 11 2008-02-10 16:26:55Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//

View File

@ -1,10 +1,9 @@
////////////////////////////////////////////////////////////////////////////////
///
/// gcc version of the x86 CPU detect routine.
/// Generic version of the x86 CPU extension detection routine.
///
/// This file is to be compiled on any platform with the GNU C compiler.
/// Compiler. Please see 'cpu_detect_x86_win.cpp' for the x86 Windows version
/// of this file.
/// This file is for GNU & other non-Windows compilers, see 'cpu_detect_x86_win.cpp'
/// for the Microsoft compiler version.
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
@ -12,10 +11,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.6 $
// Last changed : $Date: 2009-02-25 19:13:51 +0200 (Wed, 25 Feb 2009) $
// File revision : $Revision: 4 $
//
// $Id: cpu_detect_x86_gcc.cpp,v 1.6 2006/02/05 16:44:06 Olli Exp $
// $Id: cpu_detect_x86_gcc.cpp 67 2009-02-25 17:13:51Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -43,21 +42,18 @@
#include <stdexcept>
#include <string>
#include "cpu_detect.h"
#ifndef __GNUC__
#error wrong platform - this source code file is for the GNU C compiler.
#endif
#include "STTypes.h"
using namespace std;
#include <stdio.h>
//////////////////////////////////////////////////////////////////////////////
//
// processor instructions extension detection routines
//
//////////////////////////////////////////////////////////////////////////////
// Flag variable indicating whick ISA extensions are disabled (for debugging)
static uint _dwDisabledISA = 0x00; // 0xffffffff; //<- use this to disable all extensions
@ -72,8 +68,10 @@ void disableExtensions(uint dwDisableMask)
/// Checks which instruction set extensions are supported by the CPU.
uint detectCPUextensions(void)
{
#ifndef __i386__
#if (!(ALLOW_X86_OPTIMIZATIONS) || !(__GNUC__))
return 0; // always disable extensions on non-x86 platforms.
#else
uint res = 0;
@ -84,22 +82,21 @@ uint detectCPUextensions(void)
// check if 'cpuid' instructions is available by toggling eflags bit 21
"\n\tpushf" // save eflags to stack
"\n\tpop %%eax" // load eax from stack (with eflags)
"\n\tmovl (%%esp), %%eax" // load eax from stack (with eflags)
"\n\tmovl %%eax, %%ecx" // save the original eflags values to ecx
"\n\txor $0x00200000, %%eax" // toggle bit 21
"\n\tpush %%eax" // store toggled eflags to stack
"\n\tmovl %%eax, (%%esp)" // store toggled eflags to stack
"\n\tpopf" // load eflags from stack
"\n\tpushf" // save updated eflags to stack
"\n\tpop %%eax" // load from stack
"\n\tmovl (%%esp), %%eax" // load eax from stack
"\n\tpopf" // pop stack to restore esp
"\n\txor %%edx, %%edx" // clear edx for defaulting no mmx
"\n\tcmp %%ecx, %%eax" // compare to original eflags values
"\n\tjz end" // jumps to 'end' if cpuid not present
// cpuid instruction available, test for presence of mmx instructions
"\n\tmovl $1, %%eax"
"\n\tcpuid"
// movl $0x00800000, %edx // force enable MMX
"\n\ttest $0x00800000, %%edx"
"\n\tjz end" // branch if MMX not available

View File

@ -12,10 +12,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.10 $
// Last changed : $Date: 2009-02-13 18:22:48 +0200 (Fri, 13 Feb 2009) $
// File revision : $Revision: 4 $
//
// $Id: cpu_detect_x86_win.cpp,v 1.10 2006/02/05 16:44:06 Olli Exp $
// $Id: cpu_detect_x86_win.cpp 62 2009-02-13 16:22:48Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -42,7 +42,7 @@
#include "cpu_detect.h"
#ifndef _WIN32
#ifndef WIN32
#error wrong platform - this source code file is exclusively for Win32 platform
#endif
@ -78,13 +78,16 @@ uint detectCPUextensions(void)
xor esi, esi ; clear esi = result register
pushfd ; save eflags to stack
pop eax ; load eax from stack (with eflags)
mov eax,dword ptr [esp] ; load eax from stack (with eflags)
mov ecx, eax ; save the original eflags values to ecx
xor eax, 0x00200000 ; toggle bit 21
push eax ; store toggled eflags to stack
mov dword ptr [esp],eax ; store toggled eflags to stack
popfd ; load eflags from stack
pushfd ; save updated eflags to stack
pop eax ; load from stack
mov eax,dword ptr [esp] ; load eax from stack
popfd ; pop stack to restore stack pointer
xor edx, edx ; clear edx for defaulting no mmx
cmp eax, ecx ; compare to original eflags values
jz end ; jumps to 'end' if cpuid not present

View File

@ -12,7 +12,7 @@
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
/// 6.0 processor pack" update to support compiler intrinsic syntax. The update
/// is available for download at Microsoft Developers Network, see here:
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
@ -20,10 +20,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/06 18:52:43 $
// File revision : $Revision: 1.1 $
// Last changed : $Date: 2009-10-31 16:53:23 +0200 (Sat, 31 Oct 2009) $
// File revision : $Revision: 4 $
//
// $Id: mmx_optimized.cpp,v 1.1 2006/02/06 18:52:43 Olli Exp $
// $Id: mmx_optimized.cpp 75 2009-10-31 14:53:23Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -53,7 +53,7 @@
#ifdef ALLOW_MMX
// MMX routines available only with integer sample type
#if !(_WIN32 || __i386__ || __x86_64__)
#if !(WIN32 || __i386__ || __x86_64__)
#error "wrong platform - this source code file is exclusively for x86 platforms"
#endif
@ -68,6 +68,7 @@ using namespace soundtouch;
#include "TDStretch.h"
#include <mmintrin.h>
#include <limits.h>
#include <math.h>
// Calculates cross correlation of two buffers
@ -75,21 +76,21 @@ long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
{
const __m64 *pVec1, *pVec2;
__m64 shifter;
__m64 accu;
long corr;
uint i;
__m64 accu, normaccu;
long corr, norm;
int i;
pVec1 = (__m64*)pV1;
pVec2 = (__m64*)pV2;
shifter = _m_from_int(overlapDividerBits);
accu = _mm_setzero_si64();
normaccu = accu = _mm_setzero_si64();
// Process 4 parallel sets of 2 * stereo samples each during each
// round to improve CPU-level parallellization.
for (i = 0; i < overlapLength / 8; i ++)
{
__m64 temp;
__m64 temp, temp2;
// dictionary of instructions:
// _m_pmaddwd : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
@ -98,11 +99,17 @@ long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]),
_mm_madd_pi16(pVec1[1], pVec2[1]));
temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec1[0]),
_mm_madd_pi16(pVec1[1], pVec1[1]));
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]),
_mm_madd_pi16(pVec1[3], pVec2[3]));
temp2 = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec1[2]),
_mm_madd_pi16(pVec1[3], pVec1[3]));
accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));
normaccu = _mm_add_pi32(normaccu, _mm_sra_pi32(temp2, shifter));
pVec1 += 4;
pVec2 += 4;
@ -114,10 +121,16 @@ long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
corr = _m_to_int(accu);
normaccu = _mm_add_pi32(normaccu, _mm_srli_si64(normaccu, 32));
norm = _m_to_int(normaccu);
// Clear MMS state
_m_empty();
return corr;
// Normalize result by dividing by sqrt(norm) - this step is easiest
// done using floating point operation
if (norm == 0) norm = 1; // to avoid div by zero
return (long)((double)corr * USHRT_MAX / sqrt((double)norm));
// Note: Warning about the missing EMMS instruction is harmless
// as it'll be called elsewhere.
}
@ -139,7 +152,7 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
const __m64 *pVinput, *pVMidBuf;
__m64 *pVdest;
__m64 mix1, mix2, adder, shifter;
uint i;
int i;
pVinput = (const __m64*)input;
pVMidBuf = (const __m64*)pMidBuffer;
@ -154,7 +167,9 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
mix2 = _mm_add_pi16(mix1, adder);
adder = _mm_add_pi16(adder, adder);
shifter = _m_from_int(overlapDividerBits);
// Overlaplength-division by shifter. "+1" is to account for "-1" deduced in
// overlapDividerBits calculation earlier.
shifter = _m_from_int(overlapDividerBits + 1);
for (i = 0; i < overlapLength / 4; i ++)
{
@ -227,7 +242,7 @@ void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uRe
// Ensure that filter coeffs array is aligned to 16-byte boundary
delete[] filterCoeffsUnalign;
filterCoeffsUnalign = new short[2 * newLength + 8];
filterCoeffsAlign = (short *)(((ulongptr)filterCoeffsUnalign + 15) & -16);
filterCoeffsAlign = (short *)(((ulong)filterCoeffsUnalign + 15) & -16);
// rearrange the filter coefficients for mmx routines
for (i = 0;i < length; i += 4)
@ -247,7 +262,7 @@ void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uRe
// mmx-optimized version of the filter routine for stereo sound
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, const uint numSamples) const
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, uint numSamples) const
{
// Create stack copies of the needed member variables for asm routines :
uint i, j;
@ -255,7 +270,7 @@ uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, const uin
if (length < 2) return 0;
for (i = 0; i < numSamples / 2; i ++)
for (i = 0; i < (numSamples - length) / 2; i ++)
{
__m64 accu1;
__m64 accu2;

View File

@ -12,7 +12,7 @@
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
/// 6.0 processor pack" update to support SSE instruction set. The update is
/// available for download at Microsoft Developers Network, see here:
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
///
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
/// perform a search with keywords "processor pack".
@ -23,10 +23,10 @@
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.2 $
// Last changed : $Date: 2009-12-28 22:32:57 +0200 (Mon, 28 Dec 2009) $
// File revision : $Revision: 4 $
//
// $Id: sse_optimized.cpp,v 1.2 2006/02/05 16:44:06 Olli Exp $
// $Id: sse_optimized.cpp 80 2009-12-28 20:32:57Z oparviai $
//
////////////////////////////////////////////////////////////////////////////////
//
@ -68,12 +68,15 @@ using namespace soundtouch;
#include "TDStretch.h"
#include <xmmintrin.h>
#include <math.h>
// Calculates cross correlation of two buffers
double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) const
{
uint i;
__m128 vSum, *pVec2;
int i;
const float *pVec1;
const __m128 *pVec2;
__m128 vSum, vNorm;
// Note. It means a major slow-down if the routine needs to tolerate
// unaligned __m128 memory accesses. It's way faster if we can skip
@ -103,38 +106,52 @@ double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) con
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
// Note: pV2 _must_ be aligned to 16-bit boundary, pV1 need not.
pVec2 = (__m128*)pV2;
vSum = _mm_setzero_ps();
pVec1 = (const float*)pV1;
pVec2 = (const __m128*)pV2;
vSum = vNorm = _mm_setzero_ps();
// Unroll the loop by factor of 4 * 4 operations
for (i = 0; i < overlapLength / 8; i ++)
{
__m128 vTemp;
// vSum += pV1[0..3] * pV2[0..3]
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1),pVec2[0]));
vTemp = _MM_LOAD(pVec1);
vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp ,pVec2[0]));
vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
// vSum += pV1[4..7] * pV2[4..7]
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 4), pVec2[1]));
vTemp = _MM_LOAD(pVec1 + 4);
vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[1]));
vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
// vSum += pV1[8..11] * pV2[8..11]
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 8), pVec2[2]));
vTemp = _MM_LOAD(pVec1 + 8);
vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[2]));
vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
// vSum += pV1[12..15] * pV2[12..15]
vSum = _mm_add_ps(vSum, _mm_mul_ps(_MM_LOAD(pV1 + 12), pVec2[3]));
vTemp = _MM_LOAD(pVec1 + 12);
vSum = _mm_add_ps(vSum, _mm_mul_ps(vTemp, pVec2[3]));
vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp ,vTemp));
pV1 += 16;
pVec1 += 16;
pVec2 += 4;
}
// return value = vSum[0] + vSum[1] + vSum[2] + vSum[3]
float *pvSum = (float*)&vSum;
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]);
float *pvNorm = (float*)&vNorm;
double norm = sqrt(pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]);
if (norm < 1e-9) norm = 1.0; // to avoid div by zero
/* This is approximately corresponding routine in C-language:
double corr;
float *pvSum = (float*)&vSum;
return (double)(pvSum[0] + pvSum[1] + pvSum[2] + pvSum[3]) / norm;
/* This is approximately corresponding routine in C-language yet without normalization:
double corr, norm;
uint i;
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
corr = 0.0;
corr = norm = 0.0;
for (i = 0; i < overlapLength / 8; i ++)
{
corr += pV1[0] * pV2[0] +
@ -154,13 +171,16 @@ double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) con
pV1[14] * pV2[14] +
pV1[15] * pV2[15];
for (j = 0; j < 15; j ++) norm += pV1[j] * pV1[j];
pV1 += 16;
pV2 += 16;
}
return corr / sqrt(norm);
*/
/* This is corresponding routine in assembler. This may be teeny-weeny bit faster
than intrinsic version, but more difficult to maintain & get compiled on multiple
/* This is a bit outdated, corresponding routine in assembler. This may be teeny-weeny bit
faster than intrinsic version, but more difficult to maintain & get compiled on multiple
platforms.
uint overlapLengthLocal = overlapLength;
@ -239,6 +259,7 @@ double TDStretchSSE::calcCrossCorrStereo(const float *pV1, const float *pV2) con
FIRFilterSSE::FIRFilterSSE() : FIRFilter()
{
filterCoeffsAlign = NULL;
filterCoeffsUnalign = NULL;
}
@ -246,6 +267,8 @@ FIRFilterSSE::FIRFilterSSE() : FIRFilter()
FIRFilterSSE::~FIRFilterSSE()
{
delete[] filterCoeffsUnalign;
filterCoeffsAlign = NULL;
filterCoeffsUnalign = NULL;
}
@ -262,7 +285,7 @@ void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uRe
// Ensure that filter coeffs array is aligned to 16-byte boundary
delete[] filterCoeffsUnalign;
filterCoeffsUnalign = new float[2 * newLength + 4];
filterCoeffsAlign = (float *)(((unsigned long)filterCoeffsUnalign + 15) & -16);
filterCoeffsAlign = (float *)(((unsigned long)filterCoeffsUnalign + 15) & (ulong)-16);
fDivider = (float)resultDivider;
@ -279,15 +302,18 @@ void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uRe
// SSE-optimized version of the filter routine for stereo sound
uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint numSamples) const
{
int count = (numSamples - length) & -2;
int count = (int)((numSamples - length) & (uint)-2);
int j;
assert(count % 2 == 0);
if (count < 2) return 0;
assert(source != NULL);
assert(dest != NULL);
assert((length % 8) == 0);
assert(((unsigned long)filterCoeffsAlign) % 16 == 0);
assert(filterCoeffsAlign != NULL);
assert(((ulong)filterCoeffsAlign) % 16 == 0);
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
for (j = 0; j < count; j += 2)
@ -297,9 +323,9 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
__m128 sum1, sum2;
uint i;
pSrc = source; // source audio data
pFil = (__m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
// are aligned to 16-byte boundary
pSrc = (const float*)source; // source audio data
pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
// are aligned to 16-byte boundary
sum1 = sum2 = _mm_setzero_ps();
for (i = 0; i < length / 8; i ++)

View File

@ -93,9 +93,9 @@ namespace SoundtouchCfg
static const int Overlap_Min = 3;
static const int Overlap_Max = 15;
static int SequenceLenMS = 63;
static int SeekWindowMS = 16;
static int OverlapMS = 7;
static int SequenceLenMS = 50;
static int SeekWindowMS = 15;
static int OverlapMS = 25;
void ReadSettings();
void WriteSettings();

View File

@ -284,11 +284,12 @@ void SndBuffer::timeStretchWrite()
{
if( progress )
{
if( ++ts_stats_logcounter > 300 )
if( ++ts_stats_logcounter > 150 )
{
ts_stats_logcounter = 0;
ConLog( " * SPU2 > Timestretch Stats > %d%% of packets stretched.\n",
( ts_stats_stretchblocks * 100 ) / ( ts_stats_normalblocks + ts_stats_stretchblocks ) );
ConLog( " * SPU2 > Timestretch Stats > %d percent stretched. Total stretchblocks = %d.\n",
( ts_stats_stretchblocks * 100 ) / ( ts_stats_normalblocks + ts_stats_stretchblocks ),
ts_stats_stretchblocks);
ts_stats_normalblocks = 0;
ts_stats_stretchblocks = 0;
}

View File

@ -20,9 +20,9 @@
#include "SoundTouch/SoundTouch.h"
static int SequenceLenMS = 63;
static int SeekWindowMS = 16;
static int OverlapMS = 7;
static int SequenceLenMS = 50;
static int SeekWindowMS = 15;
static int OverlapMS = 25;
// Timestretch Slider Bounds, Min/Max
static const int SequenceLen_Min = 50;