mirror of https://github.com/PCSX2/pcsx2.git
3rdparty/soundtouch: Bump to v2.3.3
This commit is contained in:
parent
30e7de7555
commit
ebc3923b35
|
@ -15,8 +15,8 @@
|
||||||
|
|
||||||
<body class="normal">
|
<body class="normal">
|
||||||
<hr>
|
<hr>
|
||||||
<h1>SoundTouch audio processing library v2.3.1</h1>
|
<h1>SoundTouch audio processing library v2.3.3</h1>
|
||||||
<p class="normal">SoundTouch library Copyright © Olli Parviainen 2001-2021</p>
|
<p class="normal">SoundTouch library Copyright © Olli Parviainen 2001-2024</p>
|
||||||
<hr>
|
<hr>
|
||||||
<h2>1. Introduction </h2>
|
<h2>1. Introduction </h2>
|
||||||
<p>SoundTouch is an open-source audio processing library that allows
|
<p>SoundTouch is an open-source audio processing library that allows
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
<p>Author email: oparviai 'at' iki.fi </p>
|
<p>Author email: oparviai 'at' iki.fi </p>
|
||||||
<p>SoundTouch WWW page: <a href="http://soundtouch.surina.net">http://soundtouch.surina.net</a></p>
|
<p>SoundTouch WWW page: <a href="http://soundtouch.surina.net">http://soundtouch.surina.net</a></p>
|
||||||
<p>SoundTouch git repository: <a
|
<p>SoundTouch git repository: <a
|
||||||
href="https://gitlab.com/soundtouch/soundtouch.git">https://gitlab.com/soundtouch/soundtouch.git</a></p>
|
href="https://codeberg.org/soundtouch/soundtouch.git">https://codeberg.org/soundtouch/soundtouch.git</a></p>
|
||||||
<hr>
|
<hr>
|
||||||
<h2>2. Compiling SoundTouch</h2>
|
<h2>2. Compiling SoundTouch</h2>
|
||||||
<p>Before compiling, notice that you can choose the sample data format if it's
|
<p>Before compiling, notice that you can choose the sample data format if it's
|
||||||
|
@ -131,10 +131,12 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<b>Compiling portable Shared Library / DLL version</b>
|
<b>Compiling portable Shared Library / DLL version</b>
|
||||||
<p> The GNU autotools compilation does not automatically create a shared-library version of
|
<p> The GNU autotools compilation automatically builds an additional dynamic-link version
|
||||||
SoundTouch (.so or .dll) that features position-independent code and C-language
|
of SoundTouch library that features position-independent code and "C"-style API that is
|
||||||
api that are more suitable for cross-language development than C++ libraries.</p>
|
more suitable for calling the SoundTouch routines from other programming languages.</p>
|
||||||
<p> Use script "make-gnu-dll-sh" to build a portable dynamic library version if such is desired.</p>
|
<p>This dynamic-link library is built under source/SoundTouchDLL directory, whose
|
||||||
|
subdirectories also comtain simple example apps that use the dynamic-link library.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h4><b>2.2.2 Compiling with cmake</b></h4>
|
<h4><b>2.2.2 Compiling with cmake</b></h4>
|
||||||
<p>'cmake' build scripts are provided as an alternative to the autotools toolchain.</p>
|
<p>'cmake' build scripts are provided as an alternative to the autotools toolchain.</p>
|
||||||
|
@ -145,6 +147,9 @@
|
||||||
cmake .
|
cmake .
|
||||||
make -j
|
make -j
|
||||||
make install</pre>
|
make install</pre>
|
||||||
|
<p>To list available build options:</p>
|
||||||
|
<pre>
|
||||||
|
cmake -LH</pre>
|
||||||
<p>To compile the additional portable Shared Library / DLL version with the native C-language API:</p>
|
<p>To compile the additional portable Shared Library / DLL version with the native C-language API:</p>
|
||||||
<pre>
|
<pre>
|
||||||
cmake . -DSOUNDTOUCH_DLL=ON
|
cmake . -DSOUNDTOUCH_DLL=ON
|
||||||
|
@ -448,7 +453,7 @@
|
||||||
<h2><a name="SoundStretch"></a>4. SoundStretch audio processing utility
|
<h2><a name="SoundStretch"></a>4. SoundStretch audio processing utility
|
||||||
</h2>
|
</h2>
|
||||||
<p>SoundStretch audio processing utility<br>
|
<p>SoundStretch audio processing utility<br>
|
||||||
Copyright (c) Olli Parviainen 2002-2015</p>
|
Copyright (c) Olli Parviainen 2002-2024</p>
|
||||||
<p>SoundStretch is a simple command-line application that can change
|
<p>SoundStretch is a simple command-line application that can change
|
||||||
tempo, pitch and playback rates of WAV sound files. This program is
|
tempo, pitch and playback rates of WAV sound files. This program is
|
||||||
intended primarily to demonstrate how the "SoundTouch" library can be
|
intended primarily to demonstrate how the "SoundTouch" library can be
|
||||||
|
@ -603,6 +608,18 @@
|
||||||
<hr>
|
<hr>
|
||||||
<h2>5. Change History</h2>
|
<h2>5. Change History</h2>
|
||||||
<h3>5.1. SoundTouch library Change History </h3>
|
<h3>5.1. SoundTouch library Change History </h3>
|
||||||
|
<p><b>2.3.3:</b></p>
|
||||||
|
<ul class="current">
|
||||||
|
<li>Fixing compiler warnings, maintenance fixes to make/build files for various systems
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p><b>2.3.2:</b></p>
|
||||||
|
<ul>
|
||||||
|
<li>Improve autotools makefiles to build the `SoundTouchDLL` dynamic-link link library with
|
||||||
|
C-style API. This library variation is easier to import and use from other programming
|
||||||
|
languages than the default C++ library.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<p><b>2.3.1:</b></p>
|
<p><b>2.3.1:</b></p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Adjusted cmake build settings and header files that cmake installs</li>
|
<li>Adjusted cmake build settings and header files that cmake installs</li>
|
||||||
|
@ -622,7 +639,7 @@
|
||||||
window. This ensures that with zero tempo change the output will be same as input.
|
window. This ensures that with zero tempo change the output will be same as input.
|
||||||
</li>
|
</li>
|
||||||
<li>Bugfix: Fix a bug in TDstrectch with too small initial skipFract value that occurred
|
<li>Bugfix: Fix a bug in TDstrectch with too small initial skipFract value that occurred
|
||||||
with certain processing parameter settings: Replace assert with assignment that
|
with certain processing parameter settings: Replace assert with assignment that
|
||||||
corrects the situation.
|
corrects the situation.
|
||||||
</li>
|
</li>
|
||||||
<li>Remove OpenMP "_init_threading" workaround from Android build as it's not needed with concurrent
|
<li>Remove OpenMP "_init_threading" workaround from Android build as it's not needed with concurrent
|
||||||
|
@ -865,11 +882,14 @@
|
||||||
<li> Initial release</li>
|
<li> Initial release</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h3>5.2. SoundStretch application Change History </h3>
|
<h3>5.2. SoundStretch application Change History </h3>
|
||||||
|
<p><b>2.3.3:</b></p>
|
||||||
|
<ul class="current_soundstretch">
|
||||||
|
<li>Added support for Asian / non-latin filenames in Windows. Gnu platform has supported them already earlier.</li>
|
||||||
|
</ul>
|
||||||
<p><b>1.9:</b></p>
|
<p><b>1.9:</b></p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Added support for WAV file 'fact' information chunk.</li>
|
<li>Added support for WAV file 'fact' information chunk.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p><b>1.7.0:</b></p>
|
<p><b>1.7.0:</b></p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Bugfixes in Wavfile: exception string formatting, avoid getLengthMs() integer
|
<li>Bugfixes in Wavfile: exception string formatting, avoid getLengthMs() integer
|
||||||
|
@ -966,6 +986,7 @@
|
||||||
<li> Michael Pruett</li>
|
<li> Michael Pruett</li>
|
||||||
<li> Rajeev Puran</li>
|
<li> Rajeev Puran</li>
|
||||||
<li> RJ Ryan</li>
|
<li> RJ Ryan</li>
|
||||||
|
<li> Serge Sans Paille</li>
|
||||||
<li> John Sheehy</li>
|
<li> John Sheehy</li>
|
||||||
<li> Tim Shuttleworth</li>
|
<li> Tim Shuttleworth</li>
|
||||||
<li> Albert Sirvent</li>
|
<li> Albert Sirvent</li>
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
/// taking absolute value that's smoothed by sliding average. Signal levels that
|
/// taking absolute value that's smoothed by sliding average. Signal levels that
|
||||||
/// are below a couple of times the general RMS amplitude level are cut away to
|
/// are below a couple of times the general RMS amplitude level are cut away to
|
||||||
/// leave only notable peaks there.
|
/// leave only notable peaks there.
|
||||||
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
||||||
/// autocorrelation function of the enveloped signal.
|
/// autocorrelation function of the enveloped signal.
|
||||||
/// - After whole sound data file has been analyzed as above, the bpm level is
|
/// - After whole sound data file has been analyzed as above, the bpm level is
|
||||||
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
||||||
/// function, calculates it's precise location and converts this reading to bpm's.
|
/// function, calculates it's precise location and converts this reading to bpm's.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -137,8 +137,8 @@ namespace soundtouch
|
||||||
// 2nd order low-pass-filter
|
// 2nd order low-pass-filter
|
||||||
IIR2_filter beat_lpf;
|
IIR2_filter beat_lpf;
|
||||||
|
|
||||||
/// Updates auto-correlation function for given number of decimated samples that
|
/// 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
|
/// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
|
||||||
/// though).
|
/// though).
|
||||||
void updateXCorr(int process_samples /// How many samples are processed.
|
void updateXCorr(int process_samples /// How many samples are processed.
|
||||||
);
|
);
|
||||||
|
@ -175,9 +175,9 @@ namespace soundtouch
|
||||||
|
|
||||||
/// Inputs a block of samples for analyzing: Envelopes the samples and then
|
/// Inputs a block of samples for analyzing: Envelopes the samples and then
|
||||||
/// updates the autocorrelation estimation. When whole song data has been input
|
/// updates the autocorrelation estimation. When whole song data has been input
|
||||||
/// in smaller blocks using this function, read the resulting bpm with 'getBpm'
|
/// in smaller blocks using this function, read the resulting bpm with 'getBpm'
|
||||||
/// function.
|
/// function.
|
||||||
///
|
///
|
||||||
/// Notice that data in 'samples' array can be disrupted in processing.
|
/// Notice that data in 'samples' array can be disrupted in processing.
|
||||||
void inputSamples(const soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer
|
void inputSamples(const soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer
|
||||||
int numSamples ///< Number of samples in buffer
|
int numSamples ///< Number of samples in buffer
|
||||||
|
@ -190,13 +190,13 @@ namespace soundtouch
|
||||||
/// \return Beats-per-minute rate, or zero if detection failed.
|
/// \return Beats-per-minute rate, or zero if detection failed.
|
||||||
float getBpm();
|
float getBpm();
|
||||||
|
|
||||||
/// Get beat position arrays. Note: The array includes also really low beat detection values
|
/// Get beat position arrays. Note: The array includes also really low beat detection values
|
||||||
/// in absence of clear strong beats. Consumer may wish to filter low values away.
|
/// in absence of clear strong beats. Consumer may wish to filter low values away.
|
||||||
/// - "pos" receive array of beat positions
|
/// - "pos" receive array of beat positions
|
||||||
/// - "values" receive array of beat detection strengths
|
/// - "values" receive array of beat detection strengths
|
||||||
/// - max_num indicates max.size of "pos" and "values" array.
|
/// - max_num indicates max.size of "pos" and "values" array.
|
||||||
///
|
///
|
||||||
/// You can query a suitable array sized by calling this with NULL in "pos" & "values".
|
/// You can query a suitable array sized by calling this with nullptr in "pos" & "values".
|
||||||
///
|
///
|
||||||
/// \return number of beats in the arrays.
|
/// \return number of beats in the arrays.
|
||||||
int getBeats(float *pos, float *strength, int max_num);
|
int getBeats(float *pos, float *strength, int max_num);
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// A buffer class for temporarily storaging sound samples, operates as a
|
/// A buffer class for temporarily storaging sound samples, operates as a
|
||||||
/// first-in-first-out pipe.
|
/// first-in-first-out pipe.
|
||||||
///
|
///
|
||||||
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
||||||
/// function, and are received from the beginning of the buffer by calling
|
/// function, and are received from the beginning of the buffer by calling
|
||||||
/// the 'receiveSamples' function. The class automatically removes the
|
/// the 'receiveSamples' function. The class automatically removes the
|
||||||
/// output samples from the buffer as well as grows the storage size
|
/// output samples from the buffer as well as grows the storage size
|
||||||
/// whenever necessary.
|
/// whenever necessary.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -47,7 +47,7 @@ namespace soundtouch
|
||||||
/// Sample buffer working in FIFO (first-in-first-out) principle. The class takes
|
/// Sample buffer working in FIFO (first-in-first-out) principle. The class takes
|
||||||
/// care of storage size adjustment and data moving during input/output operations.
|
/// care of storage size adjustment and data moving during input/output operations.
|
||||||
///
|
///
|
||||||
/// Notice that in case of stereo audio, one sample is considered to consist of
|
/// Notice that in case of stereo audio, one sample is considered to consist of
|
||||||
/// both channel data.
|
/// both channel data.
|
||||||
class FIFOSampleBuffer : public FIFOSamplePipe
|
class FIFOSampleBuffer : public FIFOSamplePipe
|
||||||
{
|
{
|
||||||
|
@ -68,12 +68,12 @@ private:
|
||||||
/// Channels, 1=mono, 2=stereo.
|
/// Channels, 1=mono, 2=stereo.
|
||||||
uint channels;
|
uint channels;
|
||||||
|
|
||||||
/// Current position pointer to the buffer. This pointer is increased when samples are
|
/// Current position pointer to the buffer. This pointer is increased when samples are
|
||||||
/// removed from the pipe so that it's necessary to actually rewind buffer (move data)
|
/// removed from the pipe so that it's necessary to actually rewind buffer (move data)
|
||||||
/// only new data when is put to the pipe.
|
/// only new data when is put to the pipe.
|
||||||
uint bufferPos;
|
uint bufferPos;
|
||||||
|
|
||||||
/// Rewind the buffer by moving data from position pointed by 'bufferPos' to real
|
/// Rewind the buffer by moving data from position pointed by 'bufferPos' to real
|
||||||
/// beginning of the buffer.
|
/// beginning of the buffer.
|
||||||
void rewind();
|
void rewind();
|
||||||
|
|
||||||
|
@ -93,27 +93,27 @@ public:
|
||||||
/// destructor
|
/// destructor
|
||||||
~FIFOSampleBuffer() override;
|
~FIFOSampleBuffer() override;
|
||||||
|
|
||||||
/// Returns a pointer to the beginning of the output samples.
|
/// Returns a pointer to the beginning of the output samples.
|
||||||
/// This function is provided for accessing the output samples directly.
|
/// This function is provided for accessing the output samples directly.
|
||||||
/// Please be careful for not to corrupt the book-keeping!
|
/// Please be careful for not to corrupt the book-keeping!
|
||||||
///
|
///
|
||||||
/// When using this function to output samples, also remember to 'remove' the
|
/// When using this function to output samples, also remember to 'remove' the
|
||||||
/// output samples from the buffer by calling the
|
/// output samples from the buffer by calling the
|
||||||
/// 'receiveSamples(numSamples)' function
|
/// 'receiveSamples(numSamples)' function
|
||||||
virtual SAMPLETYPE *ptrBegin() override;
|
virtual SAMPLETYPE *ptrBegin() override;
|
||||||
|
|
||||||
/// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
/// 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
|
/// where the new samples are to be inserted). This function may be used for
|
||||||
/// inserting new samples into the sample buffer directly. Please be careful
|
/// inserting new samples into the sample buffer directly. Please be careful
|
||||||
/// not corrupt the book-keeping!
|
/// not corrupt the book-keeping!
|
||||||
///
|
///
|
||||||
/// When using this function as means for inserting new samples, also remember
|
/// When using this function as means for inserting new samples, also remember
|
||||||
/// to increase the sample count afterwards, by calling the
|
/// to increase the sample count afterwards, by calling the
|
||||||
/// 'putSamples(numSamples)' function.
|
/// 'putSamples(numSamples)' function.
|
||||||
SAMPLETYPE *ptrEnd(
|
SAMPLETYPE *ptrEnd(
|
||||||
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
||||||
///< should be so that the caller can successfully insert the
|
///< should be so that the caller can successfully insert the
|
||||||
///< desired samples to the buffer. If necessary, the function
|
///< desired samples to the buffer. If necessary, the function
|
||||||
///< grows the buffer size to comply with this requirement.
|
///< grows the buffer size to comply with this requirement.
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -123,17 +123,17 @@ public:
|
||||||
uint numSamples ///< Number of samples to insert.
|
uint numSamples ///< Number of samples to insert.
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
/// Adjusts the book-keeping to increase number of samples in the buffer without
|
/// Adjusts the book-keeping to increase number of samples in the buffer without
|
||||||
/// copying any actual samples.
|
/// copying any actual samples.
|
||||||
///
|
///
|
||||||
/// This function is used to update the number of samples in the sample buffer
|
/// This function is used to update the number of samples in the sample buffer
|
||||||
/// when accessing the buffer directly with 'ptrEnd' function. Please be
|
/// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||||
/// careful though!
|
/// careful though!
|
||||||
virtual void putSamples(uint numSamples ///< Number of samples been inserted.
|
virtual void putSamples(uint numSamples ///< Number of samples been inserted.
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
/// output buffer and removes them from the sample buffer. If there are less than
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
/// 'numsample' samples in the buffer, returns all that available.
|
/// 'numsample' samples in the buffer, returns all that available.
|
||||||
///
|
///
|
||||||
/// \return Number of samples returned.
|
/// \return Number of samples returned.
|
||||||
|
@ -141,8 +141,8 @@ public:
|
||||||
uint maxSamples ///< How many samples to receive at max.
|
uint maxSamples ///< How many samples to receive at max.
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
/// sample buffer without copying them anywhere.
|
/// sample buffer without copying them anywhere.
|
||||||
///
|
///
|
||||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
/// with 'ptrBegin' function.
|
/// with 'ptrBegin' function.
|
||||||
|
@ -156,7 +156,7 @@ public:
|
||||||
void setChannels(int numChannels);
|
void setChannels(int numChannels);
|
||||||
|
|
||||||
/// Get number of channels
|
/// Get number of channels
|
||||||
int getChannels()
|
int getChannels()
|
||||||
{
|
{
|
||||||
return channels;
|
return channels;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
/// into one end of the pipe with the 'putSamples' function, and the processed
|
/// into one end of the pipe with the 'putSamples' function, and the processed
|
||||||
/// samples are received from the other end with the 'receiveSamples' function.
|
/// samples are received from the other end with the 'receiveSamples' function.
|
||||||
///
|
///
|
||||||
/// 'FIFOProcessor' : A base class for classes the do signal processing with
|
/// 'FIFOProcessor' : A base class for classes the do signal processing with
|
||||||
/// the samples while operating like a first-in-first-out pipe. When samples
|
/// the samples while operating like a first-in-first-out pipe. When samples
|
||||||
/// are input with the 'putSamples' function, the class processes them
|
/// are input with the 'putSamples' function, the class processes them
|
||||||
/// and moves the processed samples to the given 'output' pipe object, which
|
/// and moves the processed samples to the given 'output' pipe object, which
|
||||||
|
@ -68,12 +68,12 @@ public:
|
||||||
virtual ~FIFOSamplePipe() {}
|
virtual ~FIFOSamplePipe() {}
|
||||||
|
|
||||||
|
|
||||||
/// Returns a pointer to the beginning of the output samples.
|
/// Returns a pointer to the beginning of the output samples.
|
||||||
/// This function is provided for accessing the output samples directly.
|
/// This function is provided for accessing the output samples directly.
|
||||||
/// Please be careful for not to corrupt the book-keeping!
|
/// Please be careful for not to corrupt the book-keeping!
|
||||||
///
|
///
|
||||||
/// When using this function to output samples, also remember to 'remove' the
|
/// When using this function to output samples, also remember to 'remove' the
|
||||||
/// output samples from the buffer by calling the
|
/// output samples from the buffer by calling the
|
||||||
/// 'receiveSamples(numSamples)' function
|
/// 'receiveSamples(numSamples)' function
|
||||||
virtual SAMPLETYPE *ptrBegin() = 0;
|
virtual SAMPLETYPE *ptrBegin() = 0;
|
||||||
|
|
||||||
|
@ -88,14 +88,14 @@ public:
|
||||||
void moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data.
|
void moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data.
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
int oNumSamples = other.numSamples();
|
const uint oNumSamples = other.numSamples();
|
||||||
|
|
||||||
putSamples(other.ptrBegin(), oNumSamples);
|
putSamples(other.ptrBegin(), oNumSamples);
|
||||||
other.receiveSamples(oNumSamples);
|
other.receiveSamples(oNumSamples);
|
||||||
};
|
}
|
||||||
|
|
||||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
/// output buffer and removes them from the sample buffer. If there are less than
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
/// 'numsample' samples in the buffer, returns all that available.
|
/// 'numsample' samples in the buffer, returns all that available.
|
||||||
///
|
///
|
||||||
/// \return Number of samples returned.
|
/// \return Number of samples returned.
|
||||||
|
@ -103,8 +103,8 @@ public:
|
||||||
uint maxSamples ///< How many samples to receive at max.
|
uint maxSamples ///< How many samples to receive at max.
|
||||||
) = 0;
|
) = 0;
|
||||||
|
|
||||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
/// sample buffer without copying them anywhere.
|
/// sample buffer without copying them anywhere.
|
||||||
///
|
///
|
||||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
/// with 'ptrBegin' function.
|
/// with 'ptrBegin' function.
|
||||||
|
@ -127,12 +127,12 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Base-class for sound processing routines working in FIFO principle. With this base
|
/// Base-class for sound processing routines working in FIFO principle. With this base
|
||||||
/// class it's easy to implement sound processing stages that can be chained together,
|
/// class it's easy to implement sound processing stages that can be chained together,
|
||||||
/// so that samples that are fed into beginning of the pipe automatically go through
|
/// so that samples that are fed into beginning of the pipe automatically go through
|
||||||
/// all the processing stages.
|
/// all the processing stages.
|
||||||
///
|
///
|
||||||
/// When samples are input to this class, they're first processed and then put to
|
/// When samples are input to this class, they're first processed and then put to
|
||||||
/// the FIFO pipe that's defined as output of this class. This output pipe can be
|
/// the FIFO pipe that's defined as output of this class. This output pipe can be
|
||||||
/// either other processing stage or a FIFO sample buffer.
|
/// either other processing stage or a FIFO sample buffer.
|
||||||
class FIFOProcessor :public FIFOSamplePipe
|
class FIFOProcessor :public FIFOSamplePipe
|
||||||
|
@ -144,16 +144,16 @@ protected:
|
||||||
/// Sets output pipe.
|
/// Sets output pipe.
|
||||||
void setOutPipe(FIFOSamplePipe *pOutput)
|
void setOutPipe(FIFOSamplePipe *pOutput)
|
||||||
{
|
{
|
||||||
assert(output == NULL);
|
assert(output == nullptr);
|
||||||
assert(pOutput != NULL);
|
assert(pOutput != nullptr);
|
||||||
output = pOutput;
|
output = pOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructor. Doesn't define output pipe; it has to be set be
|
/// Constructor. Doesn't define output pipe; it has to be set be
|
||||||
/// 'setOutPipe' function.
|
/// 'setOutPipe' function.
|
||||||
FIFOProcessor()
|
FIFOProcessor()
|
||||||
{
|
{
|
||||||
output = NULL;
|
output = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructor. Configures output pipe.
|
/// Constructor. Configures output pipe.
|
||||||
|
@ -168,12 +168,12 @@ protected:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a pointer to the beginning of the output samples.
|
/// Returns a pointer to the beginning of the output samples.
|
||||||
/// This function is provided for accessing the output samples directly.
|
/// This function is provided for accessing the output samples directly.
|
||||||
/// Please be careful for not to corrupt the book-keeping!
|
/// Please be careful for not to corrupt the book-keeping!
|
||||||
///
|
///
|
||||||
/// When using this function to output samples, also remember to 'remove' the
|
/// When using this function to output samples, also remember to 'remove' the
|
||||||
/// output samples from the buffer by calling the
|
/// output samples from the buffer by calling the
|
||||||
/// 'receiveSamples(numSamples)' function
|
/// 'receiveSamples(numSamples)' function
|
||||||
virtual SAMPLETYPE *ptrBegin() override
|
virtual SAMPLETYPE *ptrBegin() override
|
||||||
{
|
{
|
||||||
|
@ -182,8 +182,8 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
/// output buffer and removes them from the sample buffer. If there are less than
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
/// 'numsample' samples in the buffer, returns all that available.
|
/// 'numsample' samples in the buffer, returns all that available.
|
||||||
///
|
///
|
||||||
/// \return Number of samples returned.
|
/// \return Number of samples returned.
|
||||||
|
@ -194,8 +194,8 @@ public:
|
||||||
return output->receiveSamples(outBuffer, maxSamples);
|
return output->receiveSamples(outBuffer, maxSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
/// sample buffer without copying them anywhere.
|
/// sample buffer without copying them anywhere.
|
||||||
///
|
///
|
||||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
/// with 'ptrBegin' function.
|
/// with 'ptrBegin' function.
|
||||||
|
|
|
@ -59,15 +59,15 @@ namespace soundtouch
|
||||||
/// Max allowed number of channels
|
/// Max allowed number of channels
|
||||||
#define SOUNDTOUCH_MAX_CHANNELS 16
|
#define SOUNDTOUCH_MAX_CHANNELS 16
|
||||||
|
|
||||||
/// Activate these undef's to overrule the possible sampletype
|
/// Activate these undef's to overrule the possible sampletype
|
||||||
/// setting inherited from some other header file:
|
/// setting inherited from some other header file:
|
||||||
//#undef SOUNDTOUCH_INTEGER_SAMPLES
|
//#undef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
//#undef SOUNDTOUCH_FLOAT_SAMPLES
|
//#undef SOUNDTOUCH_FLOAT_SAMPLES
|
||||||
|
|
||||||
/// If following flag is defined, always uses multichannel processing
|
/// If following flag is defined, always uses multichannel processing
|
||||||
/// routines also for mono and stero sound. This is for routine testing
|
/// routines also for mono and stero sound. This is for routine testing
|
||||||
/// purposes; output should be same with either routines, yet disabling
|
/// purposes; output should be same with either routines, yet disabling
|
||||||
/// the dedicated mono/stereo processing routines will result in slower
|
/// the dedicated mono/stereo processing routines will result in slower
|
||||||
/// runtime performance so recommendation is to keep this off.
|
/// runtime performance so recommendation is to keep this off.
|
||||||
// #define USE_MULTICH_ALWAYS
|
// #define USE_MULTICH_ALWAYS
|
||||||
|
|
||||||
|
@ -79,31 +79,31 @@ namespace soundtouch
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !(SOUNDTOUCH_INTEGER_SAMPLES || SOUNDTOUCH_FLOAT_SAMPLES)
|
#if !(SOUNDTOUCH_INTEGER_SAMPLES || SOUNDTOUCH_FLOAT_SAMPLES)
|
||||||
|
|
||||||
/// Choose either 32bit floating point or 16bit integer sampletype
|
/// Choose either 32bit floating point or 16bit integer sampletype
|
||||||
/// by choosing one of the following defines, unless this selection
|
/// by choosing one of the following defines, unless this selection
|
||||||
/// has already been done in some other file.
|
/// has already been done in some other file.
|
||||||
////
|
////
|
||||||
/// Notes:
|
/// Notes:
|
||||||
/// - In Windows environment, choose the sample format with the
|
/// - In Windows environment, choose the sample format with the
|
||||||
/// following defines.
|
/// following defines.
|
||||||
/// - In GNU environment, the floating point samples are used by
|
/// - In GNU environment, the floating point samples are used by
|
||||||
/// default, but integer samples can be chosen by giving the
|
/// default, but integer samples can be chosen by giving the
|
||||||
/// following switch to the configure script:
|
/// following switch to the configure script:
|
||||||
/// ./configure --enable-integer-samples
|
/// ./configure --enable-integer-samples
|
||||||
/// However, if you still prefer to select the sample format here
|
/// However, if you still prefer to select the sample format here
|
||||||
/// also in GNU environment, then please #undef the INTEGER_SAMPLE
|
/// also in GNU environment, then please #undef the INTEGER_SAMPLE
|
||||||
/// and FLOAT_SAMPLE defines first as in comments above.
|
/// and FLOAT_SAMPLE defines first as in comments above.
|
||||||
//#define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples
|
//#define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples
|
||||||
#define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples
|
#define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (_M_IX86 || __i386__ || __x86_64__ || _M_X64)
|
#if (_M_IX86 || __i386__ || __x86_64__ || _M_X64)
|
||||||
/// Define this to allow X86-specific assembler/intrinsic optimizations.
|
/// Define this to allow X86-specific assembler/intrinsic optimizations.
|
||||||
/// Notice that library contains also usual C++ versions of each of these
|
/// Notice that library contains also usual C++ versions of each of these
|
||||||
/// these routines, so if you're having difficulties getting the optimized
|
/// these routines, so if you're having difficulties getting the optimized
|
||||||
/// routines compiled for whatever reason, you may disable these optimizations
|
/// routines compiled for whatever reason, you may disable these optimizations
|
||||||
/// to make the library compile.
|
/// to make the library compile.
|
||||||
|
|
||||||
#define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1
|
#define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1
|
||||||
|
@ -181,9 +181,9 @@ namespace soundtouch
|
||||||
#define ST_THROW_RT_ERROR(x) {throw std::runtime_error(x);}
|
#define ST_THROW_RT_ERROR(x) {throw std::runtime_error(x);}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// When this #define is active, eliminates a clicking sound when the "rate" or "pitch"
|
// 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.
|
// 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
|
// Default is off as such crossover is untypical case and involves a slight sound
|
||||||
// quality compromise.
|
// quality compromise.
|
||||||
//#define SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER 1
|
//#define SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER 1
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||||
///
|
///
|
||||||
/// Notes:
|
/// Notes:
|
||||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||||
/// desired tempo/pitch/rate settings with the corresponding functions.
|
/// desired tempo/pitch/rate settings with the corresponding functions.
|
||||||
///
|
///
|
||||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||||
/// samples that are to be processed are fed into one of the pipe by calling
|
/// samples that are to be processed are fed into one of the pipe by calling
|
||||||
/// function 'putSamples', while the ready processed samples can be read
|
/// function 'putSamples', while the ready processed samples can be read
|
||||||
/// from the other end of the pipeline with function 'receiveSamples'.
|
/// from the other end of the pipeline with function 'receiveSamples'.
|
||||||
///
|
///
|
||||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||||
/// samples in order to process the sound. For this reason the classes buffer
|
/// samples in order to process the sound. For this reason the classes buffer
|
||||||
/// incoming samples until there are enough of samples available for
|
/// incoming samples until there are enough of samples available for
|
||||||
/// processing, then they carry out the processing step and consequently
|
/// processing, then they carry out the processing step and consequently
|
||||||
/// make the processed samples available for outputting.
|
/// make the processed samples available for outputting.
|
||||||
///
|
///
|
||||||
/// - For the above reason, the processing routines introduce a certain
|
/// - For the above reason, the processing routines introduce a certain
|
||||||
/// 'latency' between the input and output, so that the samples input to
|
/// 'latency' between the input and output, so that the samples input to
|
||||||
/// SoundTouch may not be immediately available in the output, and neither
|
/// SoundTouch may not be immediately available in the output, and neither
|
||||||
/// the amount of outputtable samples may not immediately be in direct
|
/// the amount of outputtable samples may not immediately be in direct
|
||||||
/// relationship with the amount of previously input samples.
|
/// relationship with the amount of previously input samples.
|
||||||
///
|
///
|
||||||
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
||||||
|
@ -30,8 +30,8 @@
|
||||||
/// required.
|
/// required.
|
||||||
///
|
///
|
||||||
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
||||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||||
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
||||||
/// combining the two other controls.
|
/// combining the two other controls.
|
||||||
///
|
///
|
||||||
|
@ -72,10 +72,10 @@ namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Soundtouch library version string
|
/// Soundtouch library version string
|
||||||
#define SOUNDTOUCH_VERSION "2.3.1"
|
#define SOUNDTOUCH_VERSION "2.3.3"
|
||||||
|
|
||||||
/// SoundTouch library version id
|
/// SoundTouch library version id
|
||||||
#define SOUNDTOUCH_VERSION_ID (20301)
|
#define SOUNDTOUCH_VERSION_ID (20303)
|
||||||
|
|
||||||
//
|
//
|
||||||
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
||||||
|
@ -91,55 +91,55 @@ namespace soundtouch
|
||||||
/// quality compromising)
|
/// quality compromising)
|
||||||
#define SETTING_USE_QUICKSEEK 2
|
#define SETTING_USE_QUICKSEEK 2
|
||||||
|
|
||||||
/// Time-stretch algorithm single processing sequence length in milliseconds. This determines
|
/// Time-stretch algorithm single processing sequence length in milliseconds. This determines
|
||||||
/// to how long sequences the original sound is chopped in the time-stretch algorithm.
|
/// to how long sequences the original sound is chopped in the time-stretch algorithm.
|
||||||
/// See "STTypes.h" or README for more information.
|
/// See "STTypes.h" or README for more information.
|
||||||
#define SETTING_SEQUENCE_MS 3
|
#define SETTING_SEQUENCE_MS 3
|
||||||
|
|
||||||
/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the
|
/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the
|
||||||
/// best possible overlapping location. This determines from how wide window the algorithm
|
/// 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.
|
/// may look for an optimal joining location when mixing the sound sequences back together.
|
||||||
/// See "STTypes.h" or README for more information.
|
/// See "STTypes.h" or README for more information.
|
||||||
#define SETTING_SEEKWINDOW_MS 4
|
#define SETTING_SEEKWINDOW_MS 4
|
||||||
|
|
||||||
/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences
|
/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences
|
||||||
/// are mixed back together, to form a continuous sound stream, this parameter defines over
|
/// 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.
|
/// how long period the two consecutive sequences are let to overlap each other.
|
||||||
/// See "STTypes.h" or README for more information.
|
/// See "STTypes.h" or README for more information.
|
||||||
#define SETTING_OVERLAP_MS 5
|
#define SETTING_OVERLAP_MS 5
|
||||||
|
|
||||||
|
|
||||||
/// Call "getSetting" with this ID to query processing sequence size in samples.
|
/// Call "getSetting" with this ID to query processing sequence size in samples.
|
||||||
/// This value gives approximate value of how many input samples you'll need to
|
/// This value gives approximate value of how many input samples you'll need to
|
||||||
/// feed into SoundTouch after initial buffering to get out a new batch of
|
/// feed into SoundTouch after initial buffering to get out a new batch of
|
||||||
/// output samples.
|
/// output samples.
|
||||||
///
|
///
|
||||||
/// This value does not include initial buffering at beginning of a new processing
|
/// This value does not include initial buffering at beginning of a new processing
|
||||||
/// stream, use SETTING_INITIAL_LATENCY to get the initial buffering size.
|
/// stream, use SETTING_INITIAL_LATENCY to get the initial buffering size.
|
||||||
///
|
///
|
||||||
/// Notices:
|
/// Notices:
|
||||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||||
/// - This parameter value is not constant but change depending on
|
/// - This parameter value is not constant but change depending on
|
||||||
/// tempo/pitch/rate/samplerate settings.
|
/// tempo/pitch/rate/samplerate settings.
|
||||||
#define SETTING_NOMINAL_INPUT_SEQUENCE 6
|
#define SETTING_NOMINAL_INPUT_SEQUENCE 6
|
||||||
|
|
||||||
|
|
||||||
/// Call "getSetting" with this ID to query nominal average processing output
|
/// Call "getSetting" with this ID to query nominal average processing output
|
||||||
/// size in samples. This value tells approcimate value how many output samples
|
/// size in samples. This value tells approcimate value how many output samples
|
||||||
/// SoundTouch outputs once it does DSP processing run for a batch of input samples.
|
/// SoundTouch outputs once it does DSP processing run for a batch of input samples.
|
||||||
///
|
///
|
||||||
/// Notices:
|
/// Notices:
|
||||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||||
/// - This parameter value is not constant but change depending on
|
/// - This parameter value is not constant but change depending on
|
||||||
/// tempo/pitch/rate/samplerate settings.
|
/// tempo/pitch/rate/samplerate settings.
|
||||||
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
|
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
|
||||||
|
|
||||||
|
|
||||||
/// Call "getSetting" with this ID to query initial processing latency, i.e.
|
/// Call "getSetting" with this ID to query initial processing latency, i.e.
|
||||||
/// approx. how many samples you'll need to enter to SoundTouch pipeline before
|
/// approx. how many samples you'll need to enter to SoundTouch pipeline before
|
||||||
/// you can expect to get first batch of ready output samples out.
|
/// you can expect to get first batch of ready output samples out.
|
||||||
///
|
///
|
||||||
/// After the first output batch, you can then expect to get approx.
|
/// After the first output batch, you can then expect to get approx.
|
||||||
/// SETTING_NOMINAL_OUTPUT_SEQUENCE ready samples out for every
|
/// SETTING_NOMINAL_OUTPUT_SEQUENCE ready samples out for every
|
||||||
/// SETTING_NOMINAL_INPUT_SEQUENCE samples that you enter into SoundTouch.
|
/// SETTING_NOMINAL_INPUT_SEQUENCE samples that you enter into SoundTouch.
|
||||||
///
|
///
|
||||||
|
@ -149,18 +149,18 @@ namespace soundtouch
|
||||||
/// input sequence = 4167 samples
|
/// input sequence = 4167 samples
|
||||||
/// output sequence = 3969 samples
|
/// output sequence = 3969 samples
|
||||||
///
|
///
|
||||||
/// Accordingly, you can expect to feed in approx. 5509 samples at beginning of
|
/// Accordingly, you can expect to feed in approx. 5509 samples at beginning of
|
||||||
/// the stream, and then you'll get out the first 3969 samples. After that, for
|
/// the stream, and then you'll get out the first 3969 samples. After that, for
|
||||||
/// every approx. 4167 samples that you'll put in, you'll receive again approx.
|
/// every approx. 4167 samples that you'll put in, you'll receive again approx.
|
||||||
/// 3969 samples out.
|
/// 3969 samples out.
|
||||||
///
|
///
|
||||||
/// This also means that average latency during stream processing is
|
/// This also means that average latency during stream processing is
|
||||||
/// INITIAL_LATENCY-OUTPUT_SEQUENCE/2, in the above example case 5509-3969/2
|
/// INITIAL_LATENCY-OUTPUT_SEQUENCE/2, in the above example case 5509-3969/2
|
||||||
/// = 3524 samples
|
/// = 3524 samples
|
||||||
///
|
///
|
||||||
/// Notices:
|
/// Notices:
|
||||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||||
/// - This parameter value is not constant but change depending on
|
/// - This parameter value is not constant but change depending on
|
||||||
/// tempo/pitch/rate/samplerate settings.
|
/// tempo/pitch/rate/samplerate settings.
|
||||||
#define SETTING_INITIAL_LATENCY 8
|
#define SETTING_INITIAL_LATENCY 8
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ private:
|
||||||
/// Accumulator for how many samples in total have been read out from the processing so far
|
/// Accumulator for how many samples in total have been read out from the processing so far
|
||||||
long samplesOutput;
|
long samplesOutput;
|
||||||
|
|
||||||
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
||||||
/// 'virtualPitch' parameters.
|
/// 'virtualPitch' parameters.
|
||||||
void calcEffectiveRateAndTempo();
|
void calcEffectiveRateAndTempo();
|
||||||
|
|
||||||
|
@ -237,7 +237,7 @@ public:
|
||||||
/// represent lower pitches, larger values higher pitch.
|
/// represent lower pitches, larger values higher pitch.
|
||||||
void setPitch(double newPitch);
|
void setPitch(double newPitch);
|
||||||
|
|
||||||
/// Sets pitch change in octaves compared to the original pitch
|
/// Sets pitch change in octaves compared to the original pitch
|
||||||
/// (-1.00 .. +1.00)
|
/// (-1.00 .. +1.00)
|
||||||
void setPitchOctaves(double newPitch);
|
void setPitchOctaves(double newPitch);
|
||||||
|
|
||||||
|
@ -253,20 +253,20 @@ public:
|
||||||
void setSampleRate(uint srate);
|
void setSampleRate(uint srate);
|
||||||
|
|
||||||
/// Get ratio between input and output audio durations, useful for calculating
|
/// Get ratio between input and output audio durations, useful for calculating
|
||||||
/// processed output duration: if you'll process a stream of N samples, then
|
/// processed output duration: if you'll process a stream of N samples, then
|
||||||
/// you can expect to get out N * getInputOutputSampleRatio() samples.
|
/// you can expect to get out N * getInputOutputSampleRatio() samples.
|
||||||
///
|
///
|
||||||
/// This ratio will give accurate target duration ratio for a full audio track,
|
/// This ratio will give accurate target duration ratio for a full audio track,
|
||||||
/// given that the the whole track is processed with same processing parameters.
|
/// given that the the whole track is processed with same processing parameters.
|
||||||
///
|
///
|
||||||
/// If this ratio is applied to calculate intermediate offsets inside a processing
|
/// If this ratio is applied to calculate intermediate offsets inside a processing
|
||||||
/// stream, then this ratio is approximate and can deviate +- some tens of milliseconds
|
/// stream, then this ratio is approximate and can deviate +- some tens of milliseconds
|
||||||
/// from ideal offset, yet by end of the audio stream the duration ratio will become
|
/// from ideal offset, yet by end of the audio stream the duration ratio will become
|
||||||
/// exact.
|
/// exact.
|
||||||
///
|
///
|
||||||
/// Example: if processing with parameters "-tempo=15 -pitch=-3", the function
|
/// Example: if processing with parameters "-tempo=15 -pitch=-3", the function
|
||||||
/// will return value 0.8695652... Now, if processing an audio stream whose duration
|
/// will return value 0.8695652... Now, if processing an audio stream whose duration
|
||||||
/// is exactly one million audio samples, then you can expect the processed
|
/// is exactly one million audio samples, then you can expect the processed
|
||||||
/// output duration be 0.869565 * 1000000 = 869565 samples.
|
/// output duration be 0.869565 * 1000000 = 869565 samples.
|
||||||
double getInputOutputSampleRatio();
|
double getInputOutputSampleRatio();
|
||||||
|
|
||||||
|
@ -289,8 +289,8 @@ public:
|
||||||
///< contains data for both channels.
|
///< contains data for both channels.
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
/// output buffer and removes them from the sample buffer. If there are less than
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
/// 'numsample' samples in the buffer, returns all that available.
|
/// 'numsample' samples in the buffer, returns all that available.
|
||||||
///
|
///
|
||||||
/// \return Number of samples returned.
|
/// \return Number of samples returned.
|
||||||
|
@ -298,8 +298,8 @@ public:
|
||||||
uint maxSamples ///< How many samples to receive at max.
|
uint maxSamples ///< How many samples to receive at max.
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
/// sample buffer without copying them anywhere.
|
/// sample buffer without copying them anywhere.
|
||||||
///
|
///
|
||||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
/// with 'ptrBegin' function.
|
/// with 'ptrBegin' function.
|
||||||
|
@ -312,7 +312,7 @@ public:
|
||||||
|
|
||||||
/// Changes a setting controlling the processing system behaviour. See the
|
/// Changes a setting controlling the processing system behaviour. See the
|
||||||
/// 'SETTING_...' defines for available setting ID's.
|
/// 'SETTING_...' defines for available setting ID's.
|
||||||
///
|
///
|
||||||
/// \return 'true' if the setting was successfully changed
|
/// \return 'true' if the setting was successfully changed
|
||||||
bool setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
|
bool setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
|
||||||
int value ///< New setting value.
|
int value ///< New setting value.
|
||||||
|
@ -338,7 +338,7 @@ public:
|
||||||
/// classes 'FIFOProcessor' and 'FIFOSamplePipe')
|
/// classes 'FIFOProcessor' and 'FIFOSamplePipe')
|
||||||
///
|
///
|
||||||
/// - receiveSamples() : Use this function to receive 'ready' processed samples from SoundTouch.
|
/// - receiveSamples() : Use this function to receive 'ready' processed samples from SoundTouch.
|
||||||
/// - numSamples() : Get number of 'ready' samples that can be received with
|
/// - numSamples() : Get number of 'ready' samples that can be received with
|
||||||
/// function 'receiveSamples()'
|
/// function 'receiveSamples()'
|
||||||
/// - isEmpty() : Returns nonzero if there aren't any 'ready' samples.
|
/// - isEmpty() : Returns nonzero if there aren't any 'ready' samples.
|
||||||
/// - clear() : Clears all samples from ready/processing buffers.
|
/// - clear() : Clears all samples from ready/processing buffers.
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
///
|
||||||
|
/// Char type for SoundStretch
|
||||||
|
///
|
||||||
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
/// Author e-mail : oparviai 'at' iki.fi
|
||||||
|
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||||
|
///
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// License :
|
||||||
|
//
|
||||||
|
// SoundTouch audio processing library
|
||||||
|
// Copyright (c) Olli Parviainen
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2.1 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef SS_CHARTYPE_H
|
||||||
|
#define SS_CHARTYPE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace soundstretch
|
||||||
|
{
|
||||||
|
#if _WIN32
|
||||||
|
// wide-char types for supporting non-latin file paths in Windows
|
||||||
|
using CHARTYPE = wchar_t;
|
||||||
|
using STRING = std::wstring;
|
||||||
|
#define STRING_CONST(x) (L"" x)
|
||||||
|
#else
|
||||||
|
// gnu platform can natively support UTF-8 paths using "char*" set
|
||||||
|
using CHARTYPE = char;
|
||||||
|
using STRING = std::string;
|
||||||
|
#define STRING_CONST(x) (x)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //SS_CHARTYPE_H
|
|
@ -1,12 +1,12 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Classes for easy reading & writing of WAV sound files.
|
/// Classes for easy reading & writing of WAV sound files.
|
||||||
///
|
///
|
||||||
/// For big-endian CPU, define _BIG_ENDIAN_ during compile-time to correctly
|
/// For big-endian CPU, define _BIG_ENDIAN_ during compile-time to correctly
|
||||||
/// parse the WAV files with such processors.
|
/// parse the WAV files with such processors.
|
||||||
///
|
///
|
||||||
/// Admittingly, more complete WAV reader routines may exist in public domain,
|
/// Admittingly, more complete WAV reader routines may exist in public domain,
|
||||||
/// but the reason for 'yet another' one is that those generic WAV reader
|
/// but the reason for 'yet another' one is that those generic WAV reader
|
||||||
/// libraries are exhaustingly large and cumbersome! Wanted to have something
|
/// libraries are exhaustingly large and cumbersome! Wanted to have something
|
||||||
/// simpler here, i.e. something that's not already larger than rest of the
|
/// simpler here, i.e. something that's not already larger than rest of the
|
||||||
/// SoundTouch/SoundStretch program...
|
/// SoundTouch/SoundStretch program...
|
||||||
|
@ -42,91 +42,100 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <assert.h>
|
#include <cassert>
|
||||||
#include <limits.h>
|
#include <climits>
|
||||||
|
|
||||||
#include "WavFile.h"
|
#include "WavFile.h"
|
||||||
#include "STTypes.h"
|
#include "STTypes.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
namespace soundstretch
|
||||||
|
{
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
#define FOPEN(name, mode) _wfopen(name, STRING_CONST(mode))
|
||||||
|
#else
|
||||||
|
#define FOPEN(name, mode) fopen(name, mode)
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char riffStr[] = "RIFF";
|
static const char riffStr[] = "RIFF";
|
||||||
static const char waveStr[] = "WAVE";
|
static const char waveStr[] = "WAVE";
|
||||||
static const char fmtStr[] = "fmt ";
|
static const char fmtStr[] = "fmt ";
|
||||||
static const char factStr[] = "fact";
|
static const char factStr[] = "fact";
|
||||||
static const char dataStr[] = "data";
|
static const char dataStr[] = "data";
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Helper functions for swapping byte order to correctly read/write WAV files
|
// Helper functions for swapping byte order to correctly read/write WAV files
|
||||||
// with big-endian CPU's: Define compile-time definition _BIG_ENDIAN_ to
|
// with big-endian CPU's: Define compile-time definition _BIG_ENDIAN_ to
|
||||||
// turn-on the conversion if it appears necessary.
|
// turn-on the conversion if it appears necessary.
|
||||||
//
|
//
|
||||||
// For example, Intel x86 is little-endian and doesn't require conversion,
|
// For example, Intel x86 is little-endian and doesn't require conversion,
|
||||||
// while PowerPC of Mac's and many other RISC cpu's are big-endian.
|
// while PowerPC of Mac's and many other RISC cpu's are big-endian.
|
||||||
|
|
||||||
#ifdef BYTE_ORDER
|
#ifdef BYTE_ORDER
|
||||||
// In gcc compiler detect the byte order automatically
|
// In gcc compiler detect the byte order automatically
|
||||||
#if BYTE_ORDER == BIG_ENDIAN
|
#if BYTE_ORDER == BIG_ENDIAN
|
||||||
// big-endian platform.
|
// big-endian platform.
|
||||||
#define _BIG_ENDIAN_
|
#define _BIG_ENDIAN_
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _BIG_ENDIAN_
|
#ifdef _BIG_ENDIAN_
|
||||||
// big-endian CPU, swap bytes in 16 & 32 bit words
|
// big-endian CPU, swap bytes in 16 & 32 bit words
|
||||||
|
|
||||||
// helper-function to swap byte-order of 32bit integer
|
// helper-function to swap byte-order of 32bit integer
|
||||||
static inline int _swap32(int &dwData)
|
static inline int _swap32(int& dwData)
|
||||||
{
|
{
|
||||||
dwData = ((dwData >> 24) & 0x000000FF) |
|
dwData = ((dwData >> 24) & 0x000000FF) |
|
||||||
((dwData >> 8) & 0x0000FF00) |
|
((dwData >> 8) & 0x0000FF00) |
|
||||||
((dwData << 8) & 0x00FF0000) |
|
((dwData << 8) & 0x00FF0000) |
|
||||||
((dwData << 24) & 0xFF000000);
|
((dwData << 24) & 0xFF000000);
|
||||||
return dwData;
|
return dwData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper-function to swap byte-order of 16bit integer
|
// helper-function to swap byte-order of 16bit integer
|
||||||
static inline short _swap16(short &wData)
|
static inline short _swap16(short& wData)
|
||||||
|
{
|
||||||
|
wData = ((wData >> 8) & 0x00FF) |
|
||||||
|
((wData << 8) & 0xFF00);
|
||||||
|
return wData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper-function to swap byte-order of buffer of 16bit integers
|
||||||
|
static inline void _swap16Buffer(short* pData, int numWords)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < numWords; i++)
|
||||||
{
|
{
|
||||||
wData = ((wData >> 8) & 0x00FF) |
|
pData[i] = _swap16(pData[i]);
|
||||||
((wData << 8) & 0xFF00);
|
|
||||||
return wData;
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper-function to swap byte-order of buffer of 16bit integers
|
|
||||||
static inline void _swap16Buffer(short *pData, int numWords)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < numWords; i ++)
|
|
||||||
{
|
|
||||||
pData[i] = _swap16(pData[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#else // BIG_ENDIAN
|
#else // BIG_ENDIAN
|
||||||
// little-endian CPU, WAV file is ok as such
|
// little-endian CPU, WAV file is ok as such
|
||||||
|
|
||||||
// dummy helper-function
|
// dummy helper-function
|
||||||
static inline int _swap32(int &dwData)
|
static inline int _swap32(int& dwData)
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
return dwData;
|
return dwData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// dummy helper-function
|
// dummy helper-function
|
||||||
static inline short _swap16(short &wData)
|
static inline short _swap16(short& wData)
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
return wData;
|
return wData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// dummy helper-function
|
// dummy helper-function
|
||||||
static inline void _swap16Buffer(short *pData, int numBytes)
|
static inline void _swap16Buffer(short*, int)
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // BIG_ENDIAN
|
#endif // BIG_ENDIAN
|
||||||
|
|
||||||
|
@ -138,7 +147,7 @@ static const char dataStr[] = "data";
|
||||||
|
|
||||||
WavFileBase::WavFileBase()
|
WavFileBase::WavFileBase()
|
||||||
{
|
{
|
||||||
convBuff = NULL;
|
convBuff = nullptr;
|
||||||
convBuffSize = 0;
|
convBuffSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +160,7 @@ WavFileBase::~WavFileBase()
|
||||||
|
|
||||||
|
|
||||||
/// Get pointer to conversion buffer of at min. given size
|
/// Get pointer to conversion buffer of at min. given size
|
||||||
void *WavFileBase::getConvBuffer(int sizeBytes)
|
void* WavFileBase::getConvBuffer(int sizeBytes)
|
||||||
{
|
{
|
||||||
if (convBuffSize < sizeBytes)
|
if (convBuffSize < sizeBytes)
|
||||||
{
|
{
|
||||||
|
@ -169,32 +178,26 @@ void *WavFileBase::getConvBuffer(int sizeBytes)
|
||||||
// Class WavInFile
|
// Class WavInFile
|
||||||
//
|
//
|
||||||
|
|
||||||
WavInFile::WavInFile(const char *fileName)
|
WavInFile::WavInFile(const STRING& fileName)
|
||||||
{
|
{
|
||||||
// Try to open the file for reading
|
// Try to open the file for reading
|
||||||
fptr = fopen(fileName, "rb");
|
fptr = FOPEN(fileName.c_str(), "rb");
|
||||||
if (fptr == NULL)
|
if (fptr == nullptr)
|
||||||
{
|
{
|
||||||
// didn't succeed
|
ST_THROW_RT_ERROR("Error : Unable to open file for reading.");
|
||||||
string msg = "Error : Unable to open file \"";
|
|
||||||
msg += fileName;
|
|
||||||
msg += "\" for reading.";
|
|
||||||
ST_THROW_RT_ERROR(msg.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
WavInFile::WavInFile(FILE *file)
|
WavInFile::WavInFile(FILE* file)
|
||||||
{
|
{
|
||||||
// Try to open the file for reading
|
// Try to open the file for reading
|
||||||
fptr = file;
|
fptr = file;
|
||||||
if (!file)
|
if (!file)
|
||||||
{
|
{
|
||||||
// didn't succeed
|
ST_THROW_RT_ERROR("Error : Unable to access input stream for reading");
|
||||||
string msg = "Error : Unable to access input stream for reading";
|
|
||||||
ST_THROW_RT_ERROR(msg.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
@ -211,19 +214,17 @@ void WavInFile::init()
|
||||||
|
|
||||||
// Read the file headers
|
// Read the file headers
|
||||||
hdrsOk = readWavHeaders();
|
hdrsOk = readWavHeaders();
|
||||||
if (hdrsOk != 0)
|
if (hdrsOk != 0)
|
||||||
{
|
{
|
||||||
// Something didn't match in the wav file headers
|
|
||||||
ST_THROW_RT_ERROR("Input file is corrupt or not a WAV file");
|
ST_THROW_RT_ERROR("Input file is corrupt or not a WAV file");
|
||||||
}
|
}
|
||||||
|
|
||||||
// sanity check for format parameters
|
// sanity check for format parameters
|
||||||
if ((header.format.channel_number < 1) || (header.format.channel_number > 9) ||
|
if ((header.format.channel_number < 1) || (header.format.channel_number > 9) ||
|
||||||
(header.format.sample_rate < 4000) || (header.format.sample_rate > 192000) ||
|
(header.format.sample_rate < 4000) || (header.format.sample_rate > 192000) ||
|
||||||
(header.format.byte_per_sample < 1) || (header.format.byte_per_sample > 320) ||
|
(header.format.byte_per_sample < 1) || (header.format.byte_per_sample > 320) ||
|
||||||
(header.format.bits_per_sample < 8) || (header.format.bits_per_sample > 32))
|
(header.format.bits_per_sample < 8) || (header.format.bits_per_sample > 32))
|
||||||
{
|
{
|
||||||
// Something didn't match in the wav file headers
|
|
||||||
ST_THROW_RT_ERROR("Error: Illegal wav file header format parameters.");
|
ST_THROW_RT_ERROR("Error: Illegal wav file header format parameters.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +235,7 @@ void WavInFile::init()
|
||||||
WavInFile::~WavInFile()
|
WavInFile::~WavInFile()
|
||||||
{
|
{
|
||||||
if (fptr) fclose(fptr);
|
if (fptr) fclose(fptr);
|
||||||
fptr = NULL;
|
fptr = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -260,7 +261,7 @@ int WavInFile::checkCharTags() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int WavInFile::read(unsigned char *buffer, int maxElems)
|
int WavInFile::read(unsigned char* buffer, int maxElems)
|
||||||
{
|
{
|
||||||
int numBytes;
|
int numBytes;
|
||||||
uint afterDataRead;
|
uint afterDataRead;
|
||||||
|
@ -274,7 +275,7 @@ int WavInFile::read(unsigned char *buffer, int maxElems)
|
||||||
|
|
||||||
numBytes = maxElems;
|
numBytes = maxElems;
|
||||||
afterDataRead = dataRead + numBytes;
|
afterDataRead = dataRead + numBytes;
|
||||||
if (afterDataRead > header.data.data_len)
|
if (afterDataRead > header.data.data_len)
|
||||||
{
|
{
|
||||||
// Don't read more samples than are marked available in header
|
// Don't read more samples than are marked available in header
|
||||||
numBytes = (int)header.data.data_len - (int)dataRead;
|
numBytes = (int)header.data.data_len - (int)dataRead;
|
||||||
|
@ -289,7 +290,7 @@ int WavInFile::read(unsigned char *buffer, int maxElems)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int WavInFile::read(short *buffer, int maxElems)
|
int WavInFile::read(short* buffer, int maxElems)
|
||||||
{
|
{
|
||||||
unsigned int afterDataRead;
|
unsigned int afterDataRead;
|
||||||
int numBytes;
|
int numBytes;
|
||||||
|
@ -298,62 +299,62 @@ int WavInFile::read(short *buffer, int maxElems)
|
||||||
assert(buffer);
|
assert(buffer);
|
||||||
switch (header.format.bits_per_sample)
|
switch (header.format.bits_per_sample)
|
||||||
{
|
{
|
||||||
case 8:
|
case 8:
|
||||||
{
|
{
|
||||||
// 8 bit format
|
// 8 bit format
|
||||||
unsigned char *temp = (unsigned char*)getConvBuffer(maxElems);
|
unsigned char* temp = (unsigned char*)getConvBuffer(maxElems);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
numElems = read(temp, maxElems);
|
numElems = read(temp, maxElems);
|
||||||
// convert from 8 to 16 bit
|
// convert from 8 to 16 bit
|
||||||
for (i = 0; i < numElems; i ++)
|
for (i = 0; i < numElems; i++)
|
||||||
{
|
{
|
||||||
buffer[i] = (short)(((short)temp[i] - 128) * 256);
|
buffer[i] = (short)(((short)temp[i] - 128) * 256);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 16:
|
||||||
|
{
|
||||||
|
// 16 bit format
|
||||||
|
|
||||||
|
assert(sizeof(short) == 2);
|
||||||
|
|
||||||
|
numBytes = maxElems * 2;
|
||||||
|
afterDataRead = dataRead + numBytes;
|
||||||
|
if (afterDataRead > header.data.data_len)
|
||||||
|
{
|
||||||
|
// Don't read more samples than are marked available in header
|
||||||
|
numBytes = (int)header.data.data_len - (int)dataRead;
|
||||||
|
assert(numBytes >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 16:
|
numBytes = (int)fread(buffer, 1, numBytes, fptr);
|
||||||
{
|
dataRead += numBytes;
|
||||||
// 16 bit format
|
numElems = numBytes / 2;
|
||||||
|
|
||||||
assert(sizeof(short) == 2);
|
// 16bit samples, swap byte order if necessary
|
||||||
|
_swap16Buffer((short*)buffer, numElems);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
numBytes = maxElems * 2;
|
default:
|
||||||
afterDataRead = dataRead + numBytes;
|
{
|
||||||
if (afterDataRead > header.data.data_len)
|
stringstream ss;
|
||||||
{
|
ss << "\nOnly 8/16 bit sample WAV files supported in integer compilation. Can't open WAV file with ";
|
||||||
// Don't read more samples than are marked available in header
|
ss << (int)header.format.bits_per_sample;
|
||||||
numBytes = (int)header.data.data_len - (int)dataRead;
|
ss << " bit sample format. ";
|
||||||
assert(numBytes >= 0);
|
ST_THROW_RT_ERROR(ss.str().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
numBytes = (int)fread(buffer, 1, numBytes, fptr);
|
|
||||||
dataRead += numBytes;
|
|
||||||
numElems = numBytes / 2;
|
|
||||||
|
|
||||||
// 16bit samples, swap byte order if necessary
|
|
||||||
_swap16Buffer((short *)buffer, numElems);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
stringstream ss;
|
|
||||||
ss << "\nOnly 8/16 bit sample WAV files supported in integer compilation. Can't open WAV file with ";
|
|
||||||
ss << (int)header.format.bits_per_sample;
|
|
||||||
ss << " bit sample format. ";
|
|
||||||
ST_THROW_RT_ERROR(ss.str().c_str());
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return numElems;
|
return numElems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Read data in float format. Notice that when reading in float format
|
/// Read data in float format. Notice that when reading in float format
|
||||||
/// 8/16/24/32 bit sample formats are supported
|
/// 8/16/24/32 bit sample formats are supported
|
||||||
int WavInFile::read(float *buffer, int maxElems)
|
int WavInFile::read(float* buffer, int maxElems)
|
||||||
{
|
{
|
||||||
unsigned int afterDataRead;
|
unsigned int afterDataRead;
|
||||||
int numBytes;
|
int numBytes;
|
||||||
|
@ -374,7 +375,7 @@ int WavInFile::read(float *buffer, int maxElems)
|
||||||
|
|
||||||
numBytes = maxElems * bytesPerSample;
|
numBytes = maxElems * bytesPerSample;
|
||||||
afterDataRead = dataRead + numBytes;
|
afterDataRead = dataRead + numBytes;
|
||||||
if (afterDataRead > header.data.data_len)
|
if (afterDataRead > header.data.data_len)
|
||||||
{
|
{
|
||||||
// Don't read more samples than are marked available in header
|
// Don't read more samples than are marked available in header
|
||||||
numBytes = (int)header.data.data_len - (int)dataRead;
|
numBytes = (int)header.data.data_len - (int)dataRead;
|
||||||
|
@ -382,7 +383,7 @@ int WavInFile::read(float *buffer, int maxElems)
|
||||||
}
|
}
|
||||||
|
|
||||||
// read raw data into temporary buffer
|
// read raw data into temporary buffer
|
||||||
char *temp = (char*)getConvBuffer(numBytes);
|
char* temp = (char*)getConvBuffer(numBytes);
|
||||||
numBytes = (int)fread(temp, 1, numBytes, fptr);
|
numBytes = (int)fread(temp, 1, numBytes, fptr);
|
||||||
dataRead += numBytes;
|
dataRead += numBytes;
|
||||||
|
|
||||||
|
@ -391,56 +392,56 @@ int WavInFile::read(float *buffer, int maxElems)
|
||||||
// swap byte ordert & convert to float, depending on sample format
|
// swap byte ordert & convert to float, depending on sample format
|
||||||
switch (bytesPerSample)
|
switch (bytesPerSample)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
|
{
|
||||||
|
unsigned char* temp2 = (unsigned char*)temp;
|
||||||
|
double conv = 1.0 / 128.0;
|
||||||
|
for (int i = 0; i < numElems; i++)
|
||||||
{
|
{
|
||||||
unsigned char *temp2 = (unsigned char*)temp;
|
buffer[i] = (float)(temp2[i] * conv - 1.0);
|
||||||
double conv = 1.0 / 128.0;
|
|
||||||
for (int i = 0; i < numElems; i ++)
|
|
||||||
{
|
|
||||||
buffer[i] = (float)(temp2[i] * conv - 1.0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
|
{
|
||||||
|
short* temp2 = (short*)temp;
|
||||||
|
double conv = 1.0 / 32768.0;
|
||||||
|
for (int i = 0; i < numElems; i++)
|
||||||
{
|
{
|
||||||
short *temp2 = (short*)temp;
|
short value = temp2[i];
|
||||||
double conv = 1.0 / 32768.0;
|
buffer[i] = (float)(_swap16(value) * conv);
|
||||||
for (int i = 0; i < numElems; i ++)
|
|
||||||
{
|
|
||||||
short value = temp2[i];
|
|
||||||
buffer[i] = (float)(_swap16(value) * conv);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
|
{
|
||||||
|
char* temp2 = (char*)temp;
|
||||||
|
double conv = 1.0 / 8388608.0;
|
||||||
|
for (int i = 0; i < numElems; i++)
|
||||||
{
|
{
|
||||||
char *temp2 = (char *)temp;
|
int value = *((int*)temp2);
|
||||||
double conv = 1.0 / 8388608.0;
|
value = _swap32(value) & 0x00ffffff; // take 24 bits
|
||||||
for (int i = 0; i < numElems; i ++)
|
value |= (value & 0x00800000) ? 0xff000000 : 0; // extend minus sign bits
|
||||||
{
|
buffer[i] = (float)(value * conv);
|
||||||
int value = *((int*)temp2);
|
temp2 += 3;
|
||||||
value = _swap32(value) & 0x00ffffff; // take 24 bits
|
|
||||||
value |= (value & 0x00800000) ? 0xff000000 : 0; // extend minus sign bits
|
|
||||||
buffer[i] = (float)(value * conv);
|
|
||||||
temp2 += 3;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
|
{
|
||||||
|
int* temp2 = (int*)temp;
|
||||||
|
double conv = 1.0 / 2147483648.0;
|
||||||
|
assert(sizeof(int) == 4);
|
||||||
|
for (int i = 0; i < numElems; i++)
|
||||||
{
|
{
|
||||||
int *temp2 = (int *)temp;
|
int value = temp2[i];
|
||||||
double conv = 1.0 / 2147483648.0;
|
buffer[i] = (float)(_swap32(value) * conv);
|
||||||
assert(sizeof(int) == 4);
|
|
||||||
for (int i = 0; i < numElems; i ++)
|
|
||||||
{
|
|
||||||
int value = temp2[i];
|
|
||||||
buffer[i] = (float)(_swap32(value) * conv);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return numElems;
|
return numElems;
|
||||||
|
@ -450,7 +451,7 @@ int WavInFile::read(float *buffer, int maxElems)
|
||||||
int WavInFile::eof() const
|
int WavInFile::eof() const
|
||||||
{
|
{
|
||||||
// return true if all data has been read or file eof has reached
|
// return true if all data has been read or file eof has reached
|
||||||
return (dataRead == header.data.data_len || feof(fptr));
|
return ((uint)dataRead == header.data.data_len || feof(fptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -462,15 +463,15 @@ static int isAlpha(char c)
|
||||||
|
|
||||||
|
|
||||||
// test if all characters are between a white space ' ' and little 'z'
|
// test if all characters are between a white space ' ' and little 'z'
|
||||||
static int isAlphaStr(const char *str)
|
static int isAlphaStr(const char* str)
|
||||||
{
|
{
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
c = str[0];
|
c = str[0];
|
||||||
while (c)
|
while (c)
|
||||||
{
|
{
|
||||||
if (isAlpha(c) == 0) return 0;
|
if (isAlpha(c) == 0) return 0;
|
||||||
str ++;
|
str++;
|
||||||
c = str[0];
|
c = str[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,7 +484,7 @@ int WavInFile::readRIFFBlock()
|
||||||
if (fread(&(header.riff), sizeof(WavRiff), 1, fptr) != 1) return -1;
|
if (fread(&(header.riff), sizeof(WavRiff), 1, fptr) != 1) return -1;
|
||||||
|
|
||||||
// swap 32bit data byte order if necessary
|
// swap 32bit data byte order if necessary
|
||||||
_swap32((int &)header.riff.package_len);
|
_swap32((int&)header.riff.package_len);
|
||||||
|
|
||||||
// header.riff.riff_char should equal to 'RIFF');
|
// header.riff.riff_char should equal to 'RIFF');
|
||||||
if (memcmp(riffStr, header.riff.riff_char, 4) != 0) return -1;
|
if (memcmp(riffStr, header.riff.riff_char, 4) != 0) return -1;
|
||||||
|
@ -500,7 +501,7 @@ int WavInFile::readHeaderBlock()
|
||||||
string sLabel;
|
string sLabel;
|
||||||
|
|
||||||
// lead label string
|
// lead label string
|
||||||
if (fread(label, 1, 4, fptr) !=4) return -1;
|
if (fread(label, 1, 4, fptr) != 4) return -1;
|
||||||
label[4] = 0;
|
label[4] = 0;
|
||||||
|
|
||||||
if (isAlphaStr(label) == 0) return -1; // not a valid label
|
if (isAlphaStr(label) == 0) return -1; // not a valid label
|
||||||
|
@ -510,7 +511,7 @@ int WavInFile::readHeaderBlock()
|
||||||
{
|
{
|
||||||
int nLen, nDump;
|
int nLen, nDump;
|
||||||
|
|
||||||
// 'fmt ' block
|
// 'fmt ' block
|
||||||
memcpy(header.format.fmt, fmtStr, 4);
|
memcpy(header.format.fmt, fmtStr, 4);
|
||||||
|
|
||||||
// read length of the format field
|
// read length of the format field
|
||||||
|
@ -518,7 +519,7 @@ int WavInFile::readHeaderBlock()
|
||||||
// swap byte order if necessary
|
// swap byte order if necessary
|
||||||
_swap32(nLen);
|
_swap32(nLen);
|
||||||
|
|
||||||
// calculate how much length differs from expected
|
// calculate how much length differs from expected
|
||||||
nDump = nLen - ((int)sizeof(header.format) - 8);
|
nDump = nLen - ((int)sizeof(header.format) - 8);
|
||||||
|
|
||||||
// verify that header length isn't smaller than expected structure
|
// verify that header length isn't smaller than expected structure
|
||||||
|
@ -536,12 +537,12 @@ int WavInFile::readHeaderBlock()
|
||||||
if (fread(&(header.format.fixed), nLen, 1, fptr) != 1) return -1;
|
if (fread(&(header.format.fixed), nLen, 1, fptr) != 1) return -1;
|
||||||
|
|
||||||
// swap byte order if necessary
|
// swap byte order if necessary
|
||||||
_swap16((short &)header.format.fixed); // short int fixed;
|
_swap16((short&)header.format.fixed); // short int fixed;
|
||||||
_swap16((short &)header.format.channel_number); // short int channel_number;
|
_swap16((short&)header.format.channel_number); // short int channel_number;
|
||||||
_swap32((int &)header.format.sample_rate); // int sample_rate;
|
_swap32((int&)header.format.sample_rate); // int sample_rate;
|
||||||
_swap32((int &)header.format.byte_rate); // int byte_rate;
|
_swap32((int&)header.format.byte_rate); // int byte_rate;
|
||||||
_swap16((short &)header.format.byte_per_sample); // short int byte_per_sample;
|
_swap16((short&)header.format.byte_per_sample); // short int byte_per_sample;
|
||||||
_swap16((short &)header.format.bits_per_sample); // short int bits_per_sample;
|
_swap16((short&)header.format.bits_per_sample); // short int bits_per_sample;
|
||||||
|
|
||||||
// if format_len is larger than expected, skip the extra data
|
// if format_len is larger than expected, skip the extra data
|
||||||
if (nDump > 0)
|
if (nDump > 0)
|
||||||
|
@ -555,7 +556,7 @@ int WavInFile::readHeaderBlock()
|
||||||
{
|
{
|
||||||
int nLen, nDump;
|
int nLen, nDump;
|
||||||
|
|
||||||
// 'fact' block
|
// 'fact' block
|
||||||
memcpy(header.fact.fact_field, factStr, 4);
|
memcpy(header.fact.fact_field, factStr, 4);
|
||||||
|
|
||||||
// read length of the fact field
|
// read length of the fact field
|
||||||
|
@ -581,7 +582,7 @@ int WavInFile::readHeaderBlock()
|
||||||
if (fread(&(header.fact.fact_sample_len), nLen, 1, fptr) != 1) return -1;
|
if (fread(&(header.fact.fact_sample_len), nLen, 1, fptr) != 1) return -1;
|
||||||
|
|
||||||
// swap byte order if necessary
|
// swap byte order if necessary
|
||||||
_swap32((int &)header.fact.fact_sample_len); // int sample_length;
|
_swap32((int&)header.fact.fact_sample_len); // int sample_length;
|
||||||
|
|
||||||
// if fact_len is larger than expected, skip the extra data
|
// if fact_len is larger than expected, skip the extra data
|
||||||
if (nDump > 0)
|
if (nDump > 0)
|
||||||
|
@ -598,7 +599,7 @@ int WavInFile::readHeaderBlock()
|
||||||
if (fread(&(header.data.data_len), sizeof(uint), 1, fptr) != 1) return -1;
|
if (fread(&(header.data.data_len), sizeof(uint), 1, fptr) != 1) return -1;
|
||||||
|
|
||||||
// swap byte order if necessary
|
// swap byte order if necessary
|
||||||
_swap32((int &)header.data.data_len);
|
_swap32((int&)header.data.data_len);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -611,7 +612,7 @@ int WavInFile::readHeaderBlock()
|
||||||
// read length
|
// read length
|
||||||
if (fread(&len, sizeof(len), 1, fptr) != 1) return -1;
|
if (fread(&len, sizeof(len), 1, fptr) != 1) return -1;
|
||||||
// scan through the block
|
// scan through the block
|
||||||
for (i = 0; i < len; i ++)
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
if (fread(&temp, 1, 1, fptr) != 1) return -1;
|
if (fread(&temp, 1, 1, fptr) != 1) return -1;
|
||||||
if (feof(fptr)) return -1; // unexpected eof
|
if (feof(fptr)) return -1; // unexpected eof
|
||||||
|
@ -703,17 +704,13 @@ uint WavInFile::getElapsedMS() const
|
||||||
// Class WavOutFile
|
// Class WavOutFile
|
||||||
//
|
//
|
||||||
|
|
||||||
WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int channels)
|
WavOutFile::WavOutFile(const STRING& fileName, int sampleRate, int bits, int channels)
|
||||||
{
|
{
|
||||||
bytesWritten = 0;
|
bytesWritten = 0;
|
||||||
fptr = fopen(fileName, "wb");
|
fptr = FOPEN(fileName.c_str(), "wb");
|
||||||
if (fptr == NULL)
|
if (fptr == nullptr)
|
||||||
{
|
{
|
||||||
string msg = "Error : Unable to open file \"";
|
ST_THROW_RT_ERROR("Error : Unable to open file for writing.");
|
||||||
msg += fileName;
|
|
||||||
msg += "\" for writing.";
|
|
||||||
//pmsg = msg.c_str;
|
|
||||||
ST_THROW_RT_ERROR(msg.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fillInHeader(sampleRate, bits, channels);
|
fillInHeader(sampleRate, bits, channels);
|
||||||
|
@ -721,14 +718,13 @@ WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int chann
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
WavOutFile::WavOutFile(FILE *file, int sampleRate, int bits, int channels)
|
WavOutFile::WavOutFile(FILE* file, int sampleRate, int bits, int channels)
|
||||||
{
|
{
|
||||||
bytesWritten = 0;
|
bytesWritten = 0;
|
||||||
fptr = file;
|
fptr = file;
|
||||||
if (fptr == NULL)
|
if (fptr == nullptr)
|
||||||
{
|
{
|
||||||
string msg = "Error : Unable to access output file stream.";
|
ST_THROW_RT_ERROR("Error : Unable to access output file stream.");
|
||||||
ST_THROW_RT_ERROR(msg.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fillInHeader(sampleRate, bits, channels);
|
fillInHeader(sampleRate, bits, channels);
|
||||||
|
@ -740,7 +736,7 @@ WavOutFile::~WavOutFile()
|
||||||
{
|
{
|
||||||
finishHeader();
|
finishHeader();
|
||||||
if (fptr) fclose(fptr);
|
if (fptr) fclose(fptr);
|
||||||
fptr = NULL;
|
fptr = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -788,8 +784,8 @@ void WavOutFile::finishHeader()
|
||||||
// supplement the file length into the header structure
|
// supplement the file length into the header structure
|
||||||
header.riff.package_len = bytesWritten + sizeof(WavHeader) - sizeof(WavRiff) + 4;
|
header.riff.package_len = bytesWritten + sizeof(WavHeader) - sizeof(WavRiff) + 4;
|
||||||
header.data.data_len = bytesWritten;
|
header.data.data_len = bytesWritten;
|
||||||
header.fact.fact_sample_len = bytesWritten / header.format.byte_per_sample;
|
header.fact.fact_sample_len = bytesWritten / header.format.byte_per_sample;
|
||||||
|
|
||||||
writeHeader();
|
writeHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -801,18 +797,18 @@ void WavOutFile::writeHeader()
|
||||||
|
|
||||||
// swap byte order if necessary
|
// swap byte order if necessary
|
||||||
hdrTemp = header;
|
hdrTemp = header;
|
||||||
_swap32((int &)hdrTemp.riff.package_len);
|
_swap32((int&)hdrTemp.riff.package_len);
|
||||||
_swap32((int &)hdrTemp.format.format_len);
|
_swap32((int&)hdrTemp.format.format_len);
|
||||||
_swap16((short &)hdrTemp.format.fixed);
|
_swap16((short&)hdrTemp.format.fixed);
|
||||||
_swap16((short &)hdrTemp.format.channel_number);
|
_swap16((short&)hdrTemp.format.channel_number);
|
||||||
_swap32((int &)hdrTemp.format.sample_rate);
|
_swap32((int&)hdrTemp.format.sample_rate);
|
||||||
_swap32((int &)hdrTemp.format.byte_rate);
|
_swap32((int&)hdrTemp.format.byte_rate);
|
||||||
_swap16((short &)hdrTemp.format.byte_per_sample);
|
_swap16((short&)hdrTemp.format.byte_per_sample);
|
||||||
_swap16((short &)hdrTemp.format.bits_per_sample);
|
_swap16((short&)hdrTemp.format.bits_per_sample);
|
||||||
_swap32((int &)hdrTemp.data.data_len);
|
_swap32((int&)hdrTemp.data.data_len);
|
||||||
_swap32((int &)hdrTemp.fact.fact_len);
|
_swap32((int&)hdrTemp.fact.fact_len);
|
||||||
_swap32((int &)hdrTemp.fact.fact_sample_len);
|
_swap32((int&)hdrTemp.fact.fact_sample_len);
|
||||||
|
|
||||||
// write the supplemented header in the beginning of the file
|
// write the supplemented header in the beginning of the file
|
||||||
fseek(fptr, 0, SEEK_SET);
|
fseek(fptr, 0, SEEK_SET);
|
||||||
res = (int)fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr);
|
res = (int)fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr);
|
||||||
|
@ -826,7 +822,7 @@ void WavOutFile::writeHeader()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WavOutFile::write(const unsigned char *buffer, int numElems)
|
void WavOutFile::write(const unsigned char* buffer, int numElems)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
@ -837,7 +833,7 @@ void WavOutFile::write(const unsigned char *buffer, int numElems)
|
||||||
assert(sizeof(char) == 1);
|
assert(sizeof(char) == 1);
|
||||||
|
|
||||||
res = (int)fwrite(buffer, 1, numElems, fptr);
|
res = (int)fwrite(buffer, 1, numElems, fptr);
|
||||||
if (res != numElems)
|
if (res != numElems)
|
||||||
{
|
{
|
||||||
ST_THROW_RT_ERROR("Error while writing to a wav file.");
|
ST_THROW_RT_ERROR("Error while writing to a wav file.");
|
||||||
}
|
}
|
||||||
|
@ -846,7 +842,7 @@ void WavOutFile::write(const unsigned char *buffer, int numElems)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WavOutFile::write(const short *buffer, int numElems)
|
void WavOutFile::write(const short* buffer, int numElems)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
@ -855,47 +851,47 @@ void WavOutFile::write(const short *buffer, int numElems)
|
||||||
|
|
||||||
switch (header.format.bits_per_sample)
|
switch (header.format.bits_per_sample)
|
||||||
{
|
{
|
||||||
case 8:
|
case 8:
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char* temp = (unsigned char*)getConvBuffer(numElems);
|
||||||
|
// convert from 16bit format to 8bit format
|
||||||
|
for (i = 0; i < numElems; i++)
|
||||||
{
|
{
|
||||||
int i;
|
temp[i] = (unsigned char)(buffer[i] / 256 + 128);
|
||||||
unsigned char *temp = (unsigned char *)getConvBuffer(numElems);
|
|
||||||
// convert from 16bit format to 8bit format
|
|
||||||
for (i = 0; i < numElems; i ++)
|
|
||||||
{
|
|
||||||
temp[i] = (unsigned char)(buffer[i] / 256 + 128);
|
|
||||||
}
|
|
||||||
// write in 8bit format
|
|
||||||
write(temp, numElems);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
// write in 8bit format
|
||||||
|
write(temp, numElems);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 16:
|
case 16:
|
||||||
|
{
|
||||||
|
// 16bit format
|
||||||
|
|
||||||
|
// use temp buffer to swap byte order if necessary
|
||||||
|
short* pTemp = (short*)getConvBuffer(numElems * sizeof(short));
|
||||||
|
memcpy(pTemp, buffer, (size_t)numElems * 2L);
|
||||||
|
_swap16Buffer(pTemp, numElems);
|
||||||
|
|
||||||
|
res = (int)fwrite(pTemp, 2, numElems, fptr);
|
||||||
|
|
||||||
|
if (res != numElems)
|
||||||
{
|
{
|
||||||
// 16bit format
|
ST_THROW_RT_ERROR("Error while writing to a wav file.");
|
||||||
|
|
||||||
// use temp buffer to swap byte order if necessary
|
|
||||||
short *pTemp = (short *)getConvBuffer(numElems * sizeof(short));
|
|
||||||
memcpy(pTemp, buffer, numElems * 2);
|
|
||||||
_swap16Buffer(pTemp, numElems);
|
|
||||||
|
|
||||||
res = (int)fwrite(pTemp, 2, numElems, fptr);
|
|
||||||
|
|
||||||
if (res != numElems)
|
|
||||||
{
|
|
||||||
ST_THROW_RT_ERROR("Error while writing to a wav file.");
|
|
||||||
}
|
|
||||||
bytesWritten += 2 * numElems;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
bytesWritten += 2 * numElems;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
ss << "\nOnly 8/16 bit sample WAV files supported in integer compilation. Can't open WAV file with ";
|
ss << "\nOnly 8/16 bit sample WAV files supported in integer compilation. Can't open WAV file with ";
|
||||||
ss << (int)header.format.bits_per_sample;
|
ss << (int)header.format.bits_per_sample;
|
||||||
ss << " bit sample format. ";
|
ss << " bit sample format. ";
|
||||||
ST_THROW_RT_ERROR(ss.str().c_str());
|
ST_THROW_RT_ERROR(ss.str().c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -903,10 +899,10 @@ void WavOutFile::write(const short *buffer, int numElems)
|
||||||
/// Convert from float to integer and saturate
|
/// Convert from float to integer and saturate
|
||||||
inline int saturate(float fvalue, float minval, float maxval)
|
inline int saturate(float fvalue, float minval, float maxval)
|
||||||
{
|
{
|
||||||
if (fvalue > maxval)
|
if (fvalue > maxval)
|
||||||
{
|
{
|
||||||
fvalue = maxval;
|
fvalue = maxval;
|
||||||
}
|
}
|
||||||
else if (fvalue < minval)
|
else if (fvalue < minval)
|
||||||
{
|
{
|
||||||
fvalue = minval;
|
fvalue = minval;
|
||||||
|
@ -915,7 +911,7 @@ inline int saturate(float fvalue, float minval, float maxval)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WavOutFile::write(const float *buffer, int numElems)
|
void WavOutFile::write(const float* buffer, int numElems)
|
||||||
{
|
{
|
||||||
int numBytes;
|
int numBytes;
|
||||||
int bytesPerSample;
|
int bytesPerSample;
|
||||||
|
@ -924,63 +920,65 @@ void WavOutFile::write(const float *buffer, int numElems)
|
||||||
|
|
||||||
bytesPerSample = header.format.bits_per_sample / 8;
|
bytesPerSample = header.format.bits_per_sample / 8;
|
||||||
numBytes = numElems * bytesPerSample;
|
numBytes = numElems * bytesPerSample;
|
||||||
void *temp = getConvBuffer(numBytes + 7); // round bit up to avoid buffer overrun with 24bit-value assignment
|
void* temp = getConvBuffer(numBytes + 7); // round bit up to avoid buffer overrun with 24bit-value assignment
|
||||||
|
|
||||||
switch (bytesPerSample)
|
switch (bytesPerSample)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
|
{
|
||||||
|
unsigned char* temp2 = (unsigned char*)temp;
|
||||||
|
for (int i = 0; i < numElems; i++)
|
||||||
{
|
{
|
||||||
unsigned char *temp2 = (unsigned char *)temp;
|
temp2[i] = (unsigned char)saturate(buffer[i] * 128.0f + 128.0f, 0.0f, 255.0f);
|
||||||
for (int i = 0; i < numElems; i ++)
|
|
||||||
{
|
|
||||||
temp2[i] = (unsigned char)saturate(buffer[i] * 128.0f + 128.0f, 0.0f, 255.0f);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
|
{
|
||||||
|
short* temp2 = (short*)temp;
|
||||||
|
for (int i = 0; i < numElems; i++)
|
||||||
{
|
{
|
||||||
short *temp2 = (short *)temp;
|
short value = (short)saturate(buffer[i] * 32768.0f, -32768.0f, 32767.0f);
|
||||||
for (int i = 0; i < numElems; i ++)
|
temp2[i] = _swap16(value);
|
||||||
{
|
|
||||||
short value = (short)saturate(buffer[i] * 32768.0f, -32768.0f, 32767.0f);
|
|
||||||
temp2[i] = _swap16(value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
|
{
|
||||||
|
char* temp2 = (char*)temp;
|
||||||
|
for (int i = 0; i < numElems; i++)
|
||||||
{
|
{
|
||||||
char *temp2 = (char *)temp;
|
int value = saturate(buffer[i] * 8388608.0f, -8388608.0f, 8388607.0f);
|
||||||
for (int i = 0; i < numElems; i ++)
|
*((int*)temp2) = _swap32(value);
|
||||||
{
|
temp2 += 3;
|
||||||
int value = saturate(buffer[i] * 8388608.0f, -8388608.0f, 8388607.0f);
|
|
||||||
*((int*)temp2) = _swap32(value);
|
|
||||||
temp2 += 3;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 4:
|
case 4:
|
||||||
|
{
|
||||||
|
int* temp2 = (int*)temp;
|
||||||
|
for (int i = 0; i < numElems; i++)
|
||||||
{
|
{
|
||||||
int *temp2 = (int *)temp;
|
int value = saturate(buffer[i] * 2147483648.0f, -2147483648.0f, 2147483647.0f);
|
||||||
for (int i = 0; i < numElems; i ++)
|
temp2[i] = _swap32(value);
|
||||||
{
|
|
||||||
int value = saturate(buffer[i] * 2147483648.0f, -2147483648.0f, 2147483647.0f);
|
|
||||||
temp2[i] = _swap32(value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
int res = (int)fwrite(temp, 1, numBytes, fptr);
|
int res = (int)fwrite(temp, 1, numBytes, fptr);
|
||||||
|
|
||||||
if (res != numBytes)
|
if (res != numBytes)
|
||||||
{
|
{
|
||||||
ST_THROW_RT_ERROR("Error while writing to a wav file.");
|
ST_THROW_RT_ERROR("Error while writing to a wav file.");
|
||||||
}
|
}
|
||||||
bytesWritten += numBytes;
|
bytesWritten += numBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
///
|
///
|
||||||
/// For big-endian CPU, define BIG_ENDIAN during compile-time to correctly
|
/// For big-endian CPU, define BIG_ENDIAN during compile-time to correctly
|
||||||
/// parse the WAV files with such processors.
|
/// parse the WAV files with such processors.
|
||||||
///
|
///
|
||||||
/// Admittingly, more complete WAV reader routines may exist in public domain, but
|
/// Admittingly, more complete WAV reader routines may exist in public domain, but
|
||||||
/// the reason for 'yet another' one is that those generic WAV reader libraries are
|
/// the reason for 'yet another' one is that those generic WAV reader libraries are
|
||||||
/// exhaustingly large and cumbersome! Wanted to have something simpler here, i.e.
|
/// exhaustingly large and cumbersome! Wanted to have something simpler here, i.e.
|
||||||
/// something that's not already larger than rest of the SoundTouch/SoundStretch program...
|
/// something that's not already larger than rest of the SoundTouch/SoundStretch program...
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -40,15 +40,20 @@
|
||||||
#ifndef WAVFILE_H
|
#ifndef WAVFILE_H
|
||||||
#define WAVFILE_H
|
#define WAVFILE_H
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include "SS_CharTypes.h"
|
||||||
|
|
||||||
|
namespace soundstretch
|
||||||
|
{
|
||||||
|
|
||||||
#ifndef uint
|
#ifndef uint
|
||||||
typedef unsigned int uint;
|
typedef unsigned int uint;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/// WAV audio file 'riff' section header
|
/// WAV audio file 'riff' section header
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char riff_char[4];
|
char riff_char[4];
|
||||||
uint package_len;
|
uint package_len;
|
||||||
|
@ -56,7 +61,7 @@ typedef struct
|
||||||
} WavRiff;
|
} WavRiff;
|
||||||
|
|
||||||
/// WAV audio file 'format' section header
|
/// WAV audio file 'format' section header
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char fmt[4];
|
char fmt[4];
|
||||||
unsigned int format_len;
|
unsigned int format_len;
|
||||||
|
@ -69,7 +74,7 @@ typedef struct
|
||||||
} WavFormat;
|
} WavFormat;
|
||||||
|
|
||||||
/// WAV audio file 'fact' section header
|
/// WAV audio file 'fact' section header
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char fact_field[4];
|
char fact_field[4];
|
||||||
uint fact_len;
|
uint fact_len;
|
||||||
|
@ -77,7 +82,7 @@ typedef struct
|
||||||
} WavFact;
|
} WavFact;
|
||||||
|
|
||||||
/// WAV audio file 'data' section header
|
/// WAV audio file 'data' section header
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
char data_field[4];
|
char data_field[4];
|
||||||
uint data_len;
|
uint data_len;
|
||||||
|
@ -85,7 +90,7 @@ typedef struct
|
||||||
|
|
||||||
|
|
||||||
/// WAV audio file header
|
/// WAV audio file header
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
WavRiff riff;
|
WavRiff riff;
|
||||||
WavFormat format;
|
WavFormat format;
|
||||||
|
@ -118,9 +123,6 @@ private:
|
||||||
/// File pointer.
|
/// File pointer.
|
||||||
FILE *fptr;
|
FILE *fptr;
|
||||||
|
|
||||||
/// Position within the audio stream
|
|
||||||
long position;
|
|
||||||
|
|
||||||
/// Counter of how many bytes of sample data have been read from the file.
|
/// Counter of how many bytes of sample data have been read from the file.
|
||||||
long dataRead;
|
long dataRead;
|
||||||
|
|
||||||
|
@ -148,7 +150,7 @@ private:
|
||||||
public:
|
public:
|
||||||
/// Constructor: Opens the given WAV file. If the file can't be opened,
|
/// Constructor: Opens the given WAV file. If the file can't be opened,
|
||||||
/// throws 'runtime_error' exception.
|
/// throws 'runtime_error' exception.
|
||||||
WavInFile(const char *filename);
|
WavInFile(const STRING& filename);
|
||||||
|
|
||||||
WavInFile(FILE *file);
|
WavInFile(FILE *file);
|
||||||
|
|
||||||
|
@ -164,7 +166,7 @@ public:
|
||||||
/// Get number of bits per sample, i.e. 8 or 16.
|
/// Get number of bits per sample, i.e. 8 or 16.
|
||||||
uint getNumBits() const;
|
uint getNumBits() const;
|
||||||
|
|
||||||
/// Get sample data size in bytes. Ahem, this should return same information as
|
/// Get sample data size in bytes. Ahem, this should return same information as
|
||||||
/// 'getBytesPerSample'...
|
/// 'getBytesPerSample'...
|
||||||
uint getDataSizeInBytes() const;
|
uint getDataSizeInBytes() const;
|
||||||
|
|
||||||
|
@ -173,7 +175,7 @@ public:
|
||||||
|
|
||||||
/// Get number of bytes per audio sample (e.g. 16bit stereo = 4 bytes/sample)
|
/// Get number of bytes per audio sample (e.g. 16bit stereo = 4 bytes/sample)
|
||||||
uint getBytesPerSample() const;
|
uint getBytesPerSample() const;
|
||||||
|
|
||||||
/// Get number of audio channels in the file (1=mono, 2=stereo)
|
/// Get number of audio channels in the file (1=mono, 2=stereo)
|
||||||
uint getNumChannels() const;
|
uint getNumChannels() const;
|
||||||
|
|
||||||
|
@ -186,14 +188,14 @@ public:
|
||||||
uint getElapsedMS() const;
|
uint getElapsedMS() const;
|
||||||
|
|
||||||
/// Reads audio samples from the WAV file. This routine works only for 8 bit samples.
|
/// Reads audio samples from the WAV file. This routine works only for 8 bit samples.
|
||||||
/// Reads given number of elements from the file or if end-of-file reached, as many
|
/// Reads given number of elements from the file or if end-of-file reached, as many
|
||||||
/// elements as are left in the file.
|
/// elements as are left in the file.
|
||||||
///
|
///
|
||||||
/// \return Number of 8-bit integers read from the file.
|
/// \return Number of 8-bit integers read from the file.
|
||||||
int read(unsigned char *buffer, int maxElems);
|
int read(unsigned char *buffer, int maxElems);
|
||||||
|
|
||||||
/// Reads audio samples from the WAV file to 16 bit integer format. Reads given number
|
/// Reads audio samples from the WAV file to 16 bit integer format. Reads given number
|
||||||
/// of elements from the file or if end-of-file reached, as many elements as are
|
/// of elements from the file or if end-of-file reached, as many elements as are
|
||||||
/// left in the file.
|
/// left in the file.
|
||||||
///
|
///
|
||||||
/// \return Number of 16-bit integers read from the file.
|
/// \return Number of 16-bit integers read from the file.
|
||||||
|
@ -201,7 +203,7 @@ public:
|
||||||
int maxElems ///< Size of 'buffer' array (number of array elements).
|
int maxElems ///< Size of 'buffer' array (number of array elements).
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Reads audio samples from the WAV file to floating point format, converting
|
/// Reads audio samples from the WAV file to floating point format, converting
|
||||||
/// sample values to range [-1,1[. Reads given number of elements from the file
|
/// sample values to range [-1,1[. Reads given number of elements from the file
|
||||||
/// or if end-of-file reached, as many elements as are left in the file.
|
/// or if end-of-file reached, as many elements as are left in the file.
|
||||||
/// Notice that reading in float format supports 8/16/24/32bit sample formats.
|
/// Notice that reading in float format supports 8/16/24/32bit sample formats.
|
||||||
|
@ -242,9 +244,9 @@ private:
|
||||||
void writeHeader();
|
void writeHeader();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
|
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
|
||||||
/// if file creation fails.
|
/// if file creation fails.
|
||||||
WavOutFile(const char *fileName, ///< Filename
|
WavOutFile(const STRING& fileName, ///< Filename
|
||||||
int sampleRate, ///< Sample rate (e.g. 44100 etc)
|
int sampleRate, ///< Sample rate (e.g. 44100 etc)
|
||||||
int bits, ///< Bits per sample (8 or 16 bits)
|
int bits, ///< Bits per sample (8 or 16 bits)
|
||||||
int channels ///< Number of channels (1=mono, 2=stereo)
|
int channels ///< Number of channels (1=mono, 2=stereo)
|
||||||
|
@ -255,7 +257,7 @@ public:
|
||||||
/// Destructor: Finalizes & closes the WAV file.
|
/// Destructor: Finalizes & closes the WAV file.
|
||||||
~WavOutFile();
|
~WavOutFile();
|
||||||
|
|
||||||
/// Write data to WAV file. This function works only with 8bit samples.
|
/// Write data to WAV file. This function works only with 8bit samples.
|
||||||
/// Throws a 'runtime_error' exception if writing to file fails.
|
/// Throws a 'runtime_error' exception if writing to file fails.
|
||||||
void write(const unsigned char *buffer, ///< Pointer to sample data buffer.
|
void write(const unsigned char *buffer, ///< Pointer to sample data buffer.
|
||||||
int numElems ///< How many array items are to be written to file.
|
int numElems ///< How many array items are to be written to file.
|
||||||
|
@ -274,4 +276,6 @@ public:
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// FIR low-pass (anti-alias) filter with filter coefficient design routine and
|
/// FIR low-pass (anti-alias) filter with filter coefficient design routine and
|
||||||
/// MMX optimization.
|
/// MMX optimization.
|
||||||
///
|
///
|
||||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||||
/// transposing the sample rate with interpolation.
|
/// transposing the sample rate with interpolation.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -54,7 +54,7 @@ using namespace soundtouch;
|
||||||
static void _DEBUG_SAVE_AAFIR_COEFFS(SAMPLETYPE *coeffs, int len)
|
static void _DEBUG_SAVE_AAFIR_COEFFS(SAMPLETYPE *coeffs, int len)
|
||||||
{
|
{
|
||||||
FILE *fptr = fopen("aa_filter_coeffs.txt", "wt");
|
FILE *fptr = fopen("aa_filter_coeffs.txt", "wt");
|
||||||
if (fptr == NULL) return;
|
if (fptr == nullptr) return;
|
||||||
|
|
||||||
for (int i = 0; i < len; i ++)
|
for (int i = 0; i < len; i ++)
|
||||||
{
|
{
|
||||||
|
@ -128,16 +128,16 @@ void AAFilter::calculateCoeffs()
|
||||||
tempCoeff = TWOPI / (double)length;
|
tempCoeff = TWOPI / (double)length;
|
||||||
|
|
||||||
sum = 0;
|
sum = 0;
|
||||||
for (i = 0; i < length; i ++)
|
for (i = 0; i < length; i ++)
|
||||||
{
|
{
|
||||||
cntTemp = (double)i - (double)(length / 2);
|
cntTemp = (double)i - (double)(length / 2);
|
||||||
|
|
||||||
temp = cntTemp * wc;
|
temp = cntTemp * wc;
|
||||||
if (temp != 0)
|
if (temp != 0)
|
||||||
{
|
{
|
||||||
h = sin(temp) / temp; // sinc function
|
h = sin(temp) / temp; // sinc function
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
h = 1.0;
|
h = 1.0;
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ void AAFilter::calculateCoeffs()
|
||||||
temp = w * h;
|
temp = w * h;
|
||||||
work[i] = temp;
|
work[i] = temp;
|
||||||
|
|
||||||
// calc net sum of coefficients
|
// calc net sum of coefficients
|
||||||
sum += temp;
|
sum += temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ void AAFilter::calculateCoeffs()
|
||||||
// divided by 16384
|
// divided by 16384
|
||||||
scaleCoeff = 16384.0f / sum;
|
scaleCoeff = 16384.0f / sum;
|
||||||
|
|
||||||
for (i = 0; i < length; i ++)
|
for (i = 0; i < length; i ++)
|
||||||
{
|
{
|
||||||
temp = work[i] * scaleCoeff;
|
temp = work[i] * scaleCoeff;
|
||||||
// scale & round to nearest integer
|
// scale & round to nearest integer
|
||||||
|
@ -182,8 +182,8 @@ void AAFilter::calculateCoeffs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Applies the filter to the given sequence of samples.
|
// Applies the filter to the given sequence of samples.
|
||||||
// Note : The amount of outputted samples is by value of 'filter length'
|
// Note : The amount of outputted samples is by value of 'filter length'
|
||||||
// smaller than the amount of input samples.
|
// smaller than the amount of input samples.
|
||||||
uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
||||||
{
|
{
|
||||||
|
@ -192,8 +192,8 @@ uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples
|
||||||
|
|
||||||
|
|
||||||
/// Applies the filter to the given src & dest pipes, so that processed amount of
|
/// Applies the filter to the given src & dest pipes, so that processed amount of
|
||||||
/// samples get removed from src, and produced amount added to dest
|
/// samples get removed from src, and produced amount added to dest
|
||||||
/// Note : The amount of outputted samples is by value of 'filter length'
|
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||||
/// smaller than the amount of input samples.
|
/// smaller than the amount of input samples.
|
||||||
uint AAFilter::evaluate(FIFOSampleBuffer &dest, FIFOSampleBuffer &src) const
|
uint AAFilter::evaluate(FIFOSampleBuffer &dest, FIFOSampleBuffer &src) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||||
/// with several performance-increasing tweaks.
|
/// with several performance-increasing tweaks.
|
||||||
///
|
///
|
||||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||||
/// transposing the sample rate with interpolation.
|
/// transposing the sample rate with interpolation.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -61,8 +61,8 @@ public:
|
||||||
|
|
||||||
~AAFilter();
|
~AAFilter();
|
||||||
|
|
||||||
/// Sets new anti-alias filter cut-off edge frequency, scaled to sampling
|
/// Sets new anti-alias filter cut-off edge frequency, scaled to sampling
|
||||||
/// frequency (nyquist frequency = 0.5). The filter will cut off the
|
/// frequency (nyquist frequency = 0.5). The filter will cut off the
|
||||||
/// frequencies than that.
|
/// frequencies than that.
|
||||||
void setCutoffFreq(double newCutoffFreq);
|
void setCutoffFreq(double newCutoffFreq);
|
||||||
|
|
||||||
|
@ -71,19 +71,19 @@ public:
|
||||||
|
|
||||||
uint getLength() const;
|
uint getLength() const;
|
||||||
|
|
||||||
/// Applies the filter to the given sequence of samples.
|
/// Applies the filter to the given sequence of samples.
|
||||||
/// Note : The amount of outputted samples is by value of 'filter length'
|
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||||
/// smaller than the amount of input samples.
|
/// smaller than the amount of input samples.
|
||||||
uint evaluate(SAMPLETYPE *dest,
|
uint evaluate(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
uint numSamples,
|
uint numSamples,
|
||||||
uint numChannels) const;
|
uint numChannels) const;
|
||||||
|
|
||||||
/// Applies the filter to the given src & dest pipes, so that processed amount of
|
/// Applies the filter to the given src & dest pipes, so that processed amount of
|
||||||
/// samples get removed from src, and produced amount added to dest
|
/// samples get removed from src, and produced amount added to dest
|
||||||
/// Note : The amount of outputted samples is by value of 'filter length'
|
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||||
/// smaller than the amount of input samples.
|
/// smaller than the amount of input samples.
|
||||||
uint evaluate(FIFOSampleBuffer &dest,
|
uint evaluate(FIFOSampleBuffer &dest,
|
||||||
FIFOSampleBuffer &src) const;
|
FIFOSampleBuffer &src) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,10 +14,10 @@
|
||||||
/// taking absolute value that's smoothed by sliding average. Signal levels that
|
/// taking absolute value that's smoothed by sliding average. Signal levels that
|
||||||
/// are below a couple of times the general RMS amplitude level are cut away to
|
/// are below a couple of times the general RMS amplitude level are cut away to
|
||||||
/// leave only notable peaks there.
|
/// leave only notable peaks there.
|
||||||
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
||||||
/// autocorrelation function of the enveloped signal.
|
/// autocorrelation function of the enveloped signal.
|
||||||
/// - After whole sound data file has been analyzed as above, the bpm level is
|
/// - After whole sound data file has been analyzed as above, the bpm level is
|
||||||
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
||||||
/// function, calculates it's precise location and converts this reading to bpm's.
|
/// function, calculates it's precise location and converts this reading to bpm's.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -76,8 +76,8 @@ static const int XCORR_UPDATE_SEQUENCE = (int)(TARGET_SRATE / 5);
|
||||||
static const int MOVING_AVERAGE_N = 15;
|
static const int MOVING_AVERAGE_N = 15;
|
||||||
|
|
||||||
/// XCorr decay time constant, decay to half in 30 seconds
|
/// XCorr decay time constant, decay to half in 30 seconds
|
||||||
/// If it's desired to have the system adapt quicker to beat rate
|
/// If it's desired to have the system adapt quicker to beat rate
|
||||||
/// changes within a continuing music stream, then the
|
/// changes within a continuing music stream, then the
|
||||||
/// 'xcorr_decay_time_constant' value can be reduced, yet that
|
/// 'xcorr_decay_time_constant' value can be reduced, yet that
|
||||||
/// can increase possibility of glitches in bpm detection.
|
/// can increase possibility of glitches in bpm detection.
|
||||||
static const double XCORR_DECAY_TIME_CONSTANT = 30.0;
|
static const double XCORR_DECAY_TIME_CONSTANT = 30.0;
|
||||||
|
@ -233,16 +233,16 @@ BPMDetect::~BPMDetect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// convert to mono, low-pass filter & decimate to about 500 Hz.
|
/// convert to mono, low-pass filter & decimate to about 500 Hz.
|
||||||
/// return number of outputted samples.
|
/// return number of outputted samples.
|
||||||
///
|
///
|
||||||
/// Decimation is used to remove the unnecessary frequencies and thus to reduce
|
/// Decimation is used to remove the unnecessary frequencies and thus to reduce
|
||||||
/// the amount of data needed to be processed as calculating autocorrelation
|
/// the amount of data needed to be processed as calculating autocorrelation
|
||||||
/// function is a very-very heavy operation.
|
/// function is a very-very heavy operation.
|
||||||
///
|
///
|
||||||
/// Anti-alias filtering is done simply by averaging the samples. This is really a
|
/// Anti-alias filtering is done simply by averaging the samples. This is really a
|
||||||
/// poor-man's anti-alias filtering, but it's not so critical in this kind of application
|
/// poor-man's anti-alias filtering, but it's not so critical in this kind of application
|
||||||
/// (it'd also be difficult to design a high-quality filter with steep cut-off at very
|
/// (it'd also be difficult to design a high-quality filter with steep cut-off at very
|
||||||
/// narrow band)
|
/// narrow band)
|
||||||
int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
||||||
{
|
{
|
||||||
|
@ -252,7 +252,7 @@ int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
||||||
assert(channels > 0);
|
assert(channels > 0);
|
||||||
assert(decimateBy > 0);
|
assert(decimateBy > 0);
|
||||||
outcount = 0;
|
outcount = 0;
|
||||||
for (count = 0; count < numsamples; count ++)
|
for (count = 0; count < numsamples; count ++)
|
||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
|
|
||||||
|
@ -264,7 +264,7 @@ int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
||||||
src += j;
|
src += j;
|
||||||
|
|
||||||
decimateCount ++;
|
decimateCount ++;
|
||||||
if (decimateCount >= decimateBy)
|
if (decimateCount >= decimateBy)
|
||||||
{
|
{
|
||||||
// Store every Nth sample only
|
// Store every Nth sample only
|
||||||
out = (LONG_SAMPLETYPE)(decimateSum / (decimateBy * channels));
|
out = (LONG_SAMPLETYPE)(decimateSum / (decimateBy * channels));
|
||||||
|
@ -272,11 +272,11 @@ int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
||||||
decimateCount = 0;
|
decimateCount = 0;
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
// check ranges for sure (shouldn't actually be necessary)
|
// check ranges for sure (shouldn't actually be necessary)
|
||||||
if (out > 32767)
|
if (out > 32767)
|
||||||
{
|
{
|
||||||
out = 32767;
|
out = 32767;
|
||||||
}
|
}
|
||||||
else if (out < -32768)
|
else if (out < -32768)
|
||||||
{
|
{
|
||||||
out = -32768;
|
out = -32768;
|
||||||
}
|
}
|
||||||
|
@ -294,7 +294,7 @@ void BPMDetect::updateXCorr(int process_samples)
|
||||||
{
|
{
|
||||||
int offs;
|
int offs;
|
||||||
SAMPLETYPE *pBuffer;
|
SAMPLETYPE *pBuffer;
|
||||||
|
|
||||||
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
||||||
assert(process_samples == XCORR_UPDATE_SEQUENCE);
|
assert(process_samples == XCORR_UPDATE_SEQUENCE);
|
||||||
|
|
||||||
|
@ -311,13 +311,13 @@ void BPMDetect::updateXCorr(int process_samples)
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for (offs = windowStart; offs < windowLen; offs ++)
|
for (offs = windowStart; offs < windowLen; offs ++)
|
||||||
{
|
{
|
||||||
float sum;
|
float sum;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
sum = 0;
|
sum = 0;
|
||||||
for (i = 0; i < process_samples; i ++)
|
for (i = 0; i < process_samples; i ++)
|
||||||
{
|
{
|
||||||
sum += tmp[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
|
sum += tmp[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
|
||||||
}
|
}
|
||||||
|
@ -376,8 +376,6 @@ void BPMDetect::updateBeatPos(int process_samples)
|
||||||
// detect beats
|
// detect beats
|
||||||
for (int i = 0; i < skipstep; i++)
|
for (int i = 0; i < skipstep; i++)
|
||||||
{
|
{
|
||||||
LONG_SAMPLETYPE max = 0;
|
|
||||||
|
|
||||||
float sum = beatcorr_ringbuff[beatcorr_ringbuffpos];
|
float sum = beatcorr_ringbuff[beatcorr_ringbuffpos];
|
||||||
sum -= beat_lpf.update(sum);
|
sum -= beat_lpf.update(sum);
|
||||||
|
|
||||||
|
@ -433,7 +431,7 @@ void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
|
||||||
|
|
||||||
// when the buffer has enough samples for processing...
|
// when the buffer has enough samples for processing...
|
||||||
int req = max(windowLen + XCORR_UPDATE_SEQUENCE, 2 * XCORR_UPDATE_SEQUENCE);
|
int req = max(windowLen + XCORR_UPDATE_SEQUENCE, 2 * XCORR_UPDATE_SEQUENCE);
|
||||||
while ((int)buffer->numSamples() >= req)
|
while ((int)buffer->numSamples() >= req)
|
||||||
{
|
{
|
||||||
// ... update autocorrelations...
|
// ... update autocorrelations...
|
||||||
updateXCorr(XCORR_UPDATE_SEQUENCE);
|
updateXCorr(XCORR_UPDATE_SEQUENCE);
|
||||||
|
@ -504,7 +502,7 @@ void MAFilter(float *dest, const float *source, int start, int end, int N)
|
||||||
|
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
for (int j = i1; j < i2; j ++)
|
for (int j = i1; j < i2; j ++)
|
||||||
{
|
{
|
||||||
sum += source[j];
|
sum += source[j];
|
||||||
}
|
}
|
||||||
dest[i] = (float)(sum / (i2 - i1));
|
dest[i] = (float)(sum / (i2 - i1));
|
||||||
|
@ -550,19 +548,19 @@ float BPMDetect::getBpm()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Get beat position arrays. Note: The array includes also really low beat detection values
|
/// Get beat position arrays. Note: The array includes also really low beat detection values
|
||||||
/// in absence of clear strong beats. Consumer may wish to filter low values away.
|
/// in absence of clear strong beats. Consumer may wish to filter low values away.
|
||||||
/// - "pos" receive array of beat positions
|
/// - "pos" receive array of beat positions
|
||||||
/// - "values" receive array of beat detection strengths
|
/// - "values" receive array of beat detection strengths
|
||||||
/// - max_num indicates max.size of "pos" and "values" array.
|
/// - max_num indicates max.size of "pos" and "values" array.
|
||||||
///
|
///
|
||||||
/// You can query a suitable array sized by calling this with NULL in "pos" & "values".
|
/// You can query a suitable array sized by calling this with nullptr in "pos" & "values".
|
||||||
///
|
///
|
||||||
/// \return number of beats in the arrays.
|
/// \return number of beats in the arrays.
|
||||||
int BPMDetect::getBeats(float *pos, float *values, int max_num)
|
int BPMDetect::getBeats(float *pos, float *values, int max_num)
|
||||||
{
|
{
|
||||||
int num = (int)beats.size();
|
int num = (int)beats.size();
|
||||||
if ((!pos) || (!values)) return num; // pos or values NULL, return just size
|
if ((!pos) || (!values)) return num; // pos or values nullptr, return just size
|
||||||
|
|
||||||
for (int i = 0; (i < num) && (i < max_num); i++)
|
for (int i = 0; (i < num) && (i < max_num); i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// A buffer class for temporarily storaging sound samples, operates as a
|
/// A buffer class for temporarily storaging sound samples, operates as a
|
||||||
/// first-in-first-out pipe.
|
/// first-in-first-out pipe.
|
||||||
///
|
///
|
||||||
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
||||||
/// function, and are received from the beginning of the buffer by calling
|
/// function, and are received from the beginning of the buffer by calling
|
||||||
/// the 'receiveSamples' function. The class automatically removes the
|
/// the 'receiveSamples' function. The class automatically removes the
|
||||||
/// outputted samples from the buffer, as well as grows the buffer size
|
/// outputted samples from the buffer, as well as grows the buffer size
|
||||||
/// whenever necessary.
|
/// whenever necessary.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -50,12 +50,12 @@ FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
|
||||||
{
|
{
|
||||||
assert(numChannels > 0);
|
assert(numChannels > 0);
|
||||||
sizeInBytes = 0; // reasonable initial value
|
sizeInBytes = 0; // reasonable initial value
|
||||||
buffer = NULL;
|
buffer = nullptr;
|
||||||
bufferUnaligned = NULL;
|
bufferUnaligned = nullptr;
|
||||||
samplesInBuffer = 0;
|
samplesInBuffer = 0;
|
||||||
bufferPos = 0;
|
bufferPos = 0;
|
||||||
channels = (uint)numChannels;
|
channels = (uint)numChannels;
|
||||||
ensureCapacity(32); // allocate initial capacity
|
ensureCapacity(32); // allocate initial capacity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,8 +63,8 @@ FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
|
||||||
FIFOSampleBuffer::~FIFOSampleBuffer()
|
FIFOSampleBuffer::~FIFOSampleBuffer()
|
||||||
{
|
{
|
||||||
delete[] bufferUnaligned;
|
delete[] bufferUnaligned;
|
||||||
bufferUnaligned = NULL;
|
bufferUnaligned = nullptr;
|
||||||
buffer = NULL;
|
buffer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,11 +82,11 @@ void FIFOSampleBuffer::setChannels(int numChannels)
|
||||||
|
|
||||||
|
|
||||||
// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and
|
// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and
|
||||||
// zeroes this pointer by copying samples from the 'bufferPos' pointer
|
// zeroes this pointer by copying samples from the 'bufferPos' pointer
|
||||||
// location on to the beginning of the buffer.
|
// location on to the beginning of the buffer.
|
||||||
void FIFOSampleBuffer::rewind()
|
void FIFOSampleBuffer::rewind()
|
||||||
{
|
{
|
||||||
if (buffer && bufferPos)
|
if (buffer && bufferPos)
|
||||||
{
|
{
|
||||||
memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
|
memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
|
||||||
bufferPos = 0;
|
bufferPos = 0;
|
||||||
|
@ -94,7 +94,7 @@ void FIFOSampleBuffer::rewind()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
||||||
// the sample buffer.
|
// the sample buffer.
|
||||||
void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
{
|
{
|
||||||
|
@ -107,7 +107,7 @@ void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
// samples.
|
// samples.
|
||||||
//
|
//
|
||||||
// This function is used to update the number of samples in the sample buffer
|
// This function is used to update the number of samples in the sample buffer
|
||||||
// when accessing the buffer directly with 'ptrEnd' function. Please be
|
// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||||
// careful though!
|
// careful though!
|
||||||
void FIFOSampleBuffer::putSamples(uint nSamples)
|
void FIFOSampleBuffer::putSamples(uint nSamples)
|
||||||
{
|
{
|
||||||
|
@ -119,31 +119,31 @@ void FIFOSampleBuffer::putSamples(uint nSamples)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
// 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
|
// where the new samples are to be inserted). This function may be used for
|
||||||
// inserting new samples into the sample buffer directly. Please be careful!
|
// inserting new samples into the sample buffer directly. Please be careful!
|
||||||
//
|
//
|
||||||
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
||||||
// terms of samples) there _at least_ should be, in order to the caller to
|
// terms of samples) there _at least_ should be, in order to the caller to
|
||||||
// successfully insert all the required samples to the buffer. When necessary,
|
// successfully insert all the required samples to the buffer. When necessary,
|
||||||
// the function grows the buffer size to comply with this requirement.
|
// the function grows the buffer size to comply with this requirement.
|
||||||
//
|
//
|
||||||
// When using this function as means for inserting new samples, also remember
|
// When using this function as means for inserting new samples, also remember
|
||||||
// to increase the sample count afterwards, by calling the
|
// to increase the sample count afterwards, by calling the
|
||||||
// 'putSamples(numSamples)' function.
|
// 'putSamples(numSamples)' function.
|
||||||
SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
|
SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
|
||||||
{
|
{
|
||||||
ensureCapacity(samplesInBuffer + slackCapacity);
|
ensureCapacity(samplesInBuffer + slackCapacity);
|
||||||
return buffer + samplesInBuffer * channels;
|
return buffer + samplesInBuffer * channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Returns a pointer to the beginning of the currently non-outputted samples.
|
// Returns a pointer to the beginning of the currently non-outputted samples.
|
||||||
// This function is provided for accessing the output samples directly.
|
// This function is provided for accessing the output samples directly.
|
||||||
// Please be careful!
|
// Please be careful!
|
||||||
//
|
//
|
||||||
// When using this function to output samples, also remember to 'remove' the
|
// When using this function to output samples, also remember to 'remove' the
|
||||||
// outputted samples from the buffer by calling the
|
// outputted samples from the buffer by calling the
|
||||||
// 'receiveSamples(numSamples)' function
|
// 'receiveSamples(numSamples)' function
|
||||||
SAMPLETYPE *FIFOSampleBuffer::ptrBegin()
|
SAMPLETYPE *FIFOSampleBuffer::ptrBegin()
|
||||||
{
|
{
|
||||||
|
@ -160,13 +160,13 @@ void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
||||||
{
|
{
|
||||||
SAMPLETYPE *tempUnaligned, *temp;
|
SAMPLETYPE *tempUnaligned, *temp;
|
||||||
|
|
||||||
if (capacityRequirement > getCapacity())
|
if (capacityRequirement > getCapacity())
|
||||||
{
|
{
|
||||||
// enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
|
// enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
|
||||||
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
|
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
|
||||||
assert(sizeInBytes % 2 == 0);
|
assert(sizeInBytes % 2 == 0);
|
||||||
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
|
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
|
||||||
if (tempUnaligned == NULL)
|
if (tempUnaligned == nullptr)
|
||||||
{
|
{
|
||||||
ST_THROW_RT_ERROR("Couldn't allocate memory!\n");
|
ST_THROW_RT_ERROR("Couldn't allocate memory!\n");
|
||||||
}
|
}
|
||||||
|
@ -180,8 +180,8 @@ void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
||||||
buffer = temp;
|
buffer = temp;
|
||||||
bufferUnaligned = tempUnaligned;
|
bufferUnaligned = tempUnaligned;
|
||||||
bufferPos = 0;
|
bufferPos = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// simply rewind the buffer (if necessary)
|
// simply rewind the buffer (if necessary)
|
||||||
rewind();
|
rewind();
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// General FIR digital filter routines with MMX optimization.
|
/// General FIR digital filter routines with MMX optimization.
|
||||||
///
|
///
|
||||||
/// Notes : MMX optimized functions reside in a separate, platform-specific file,
|
/// Notes : MMX optimized functions reside in a separate, platform-specific file,
|
||||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||||
///
|
///
|
||||||
/// This source file contains OpenMP optimizations that allow speeding up the
|
/// This source file contains OpenMP optimizations that allow speeding up the
|
||||||
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
||||||
/// in parallel. See the following article link for more detailed discussion
|
/// in parallel. See the following article link for more detailed discussion
|
||||||
/// about SoundTouch OpenMP optimizations:
|
/// about SoundTouch OpenMP optimizations:
|
||||||
/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices
|
/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices
|
||||||
///
|
///
|
||||||
|
@ -59,8 +59,8 @@ FIRFilter::FIRFilter()
|
||||||
resultDivider = 0;
|
resultDivider = 0;
|
||||||
length = 0;
|
length = 0;
|
||||||
lengthDiv8 = 0;
|
lengthDiv8 = 0;
|
||||||
filterCoeffs = NULL;
|
filterCoeffs = nullptr;
|
||||||
filterCoeffsStereo = NULL;
|
filterCoeffsStereo = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -75,20 +75,16 @@ FIRFilter::~FIRFilter()
|
||||||
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||||
{
|
{
|
||||||
int j, end;
|
int j, end;
|
||||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
|
||||||
// when using floating point samples, use a scaler instead of a divider
|
|
||||||
// because division is much slower operation than multiplying.
|
|
||||||
double dScaler = 1.0 / (double)resultDivider;
|
|
||||||
#endif
|
|
||||||
// hint compiler autovectorization that loop length is divisible by 8
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
int ilength = length & -8;
|
uint ilength = length & -8;
|
||||||
|
|
||||||
assert((length != 0) && (length == ilength) && (src != NULL) && (dest != NULL) && (filterCoeffs != NULL));
|
assert((length != 0) && (length == ilength) && (src != nullptr) && (dest != nullptr) && (filterCoeffs != nullptr));
|
||||||
|
assert(numSamples > ilength);
|
||||||
|
|
||||||
end = 2 * (numSamples - ilength);
|
end = 2 * (numSamples - ilength);
|
||||||
|
|
||||||
#pragma omp parallel for
|
#pragma omp parallel for
|
||||||
for (j = 0; j < end; j += 2)
|
for (j = 0; j < end; j += 2)
|
||||||
{
|
{
|
||||||
const SAMPLETYPE *ptr;
|
const SAMPLETYPE *ptr;
|
||||||
LONG_SAMPLETYPE suml, sumr;
|
LONG_SAMPLETYPE suml, sumr;
|
||||||
|
@ -96,7 +92,7 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
||||||
suml = sumr = 0;
|
suml = sumr = 0;
|
||||||
ptr = src + j;
|
ptr = src + j;
|
||||||
|
|
||||||
for (int i = 0; i < ilength; i ++)
|
for (uint i = 0; i < ilength; i ++)
|
||||||
{
|
{
|
||||||
suml += ptr[2 * i] * filterCoeffsStereo[2 * i];
|
suml += ptr[2 * i] * filterCoeffsStereo[2 * i];
|
||||||
sumr += ptr[2 * i + 1] * filterCoeffsStereo[2 * i + 1];
|
sumr += ptr[2 * i + 1] * filterCoeffsStereo[2 * i + 1];
|
||||||
|
@ -121,11 +117,6 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
||||||
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||||
{
|
{
|
||||||
int j, end;
|
int j, end;
|
||||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
|
||||||
// when using floating point samples, use a scaler instead of a divider
|
|
||||||
// because division is much slower operation than multiplying.
|
|
||||||
double dScaler = 1.0 / (double)resultDivider;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// hint compiler autovectorization that loop length is divisible by 8
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
int ilength = length & -8;
|
int ilength = length & -8;
|
||||||
|
@ -160,16 +151,10 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||||
{
|
{
|
||||||
int j, end;
|
int j, end;
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
|
||||||
// when using floating point samples, use a scaler instead of a divider
|
|
||||||
// because division is much slower operation than multiplying.
|
|
||||||
double dScaler = 1.0 / (double)resultDivider;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
assert(length != 0);
|
assert(length != 0);
|
||||||
assert(src != NULL);
|
assert(src != nullptr);
|
||||||
assert(dest != NULL);
|
assert(dest != nullptr);
|
||||||
assert(filterCoeffs != NULL);
|
assert(filterCoeffs != nullptr);
|
||||||
assert(numChannels < 16);
|
assert(numChannels < 16);
|
||||||
|
|
||||||
// hint compiler autovectorization that loop length is divisible by 8
|
// hint compiler autovectorization that loop length is divisible by 8
|
||||||
|
@ -201,7 +186,7 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||||
ptr ++;
|
ptr ++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (c = 0; c < numChannels; c ++)
|
for (c = 0; c < numChannels; c ++)
|
||||||
{
|
{
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
@ -257,11 +242,11 @@ uint FIRFilter::getLength() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Applies the filter to the given sequence of samples.
|
// Applies the filter to the given sequence of samples.
|
||||||
//
|
//
|
||||||
// Note : The amount of outputted samples is by value of 'filter_length'
|
// Note : The amount of outputted samples is by value of 'filter_length'
|
||||||
// smaller than the amount of input samples.
|
// smaller than the amount of input samples.
|
||||||
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels)
|
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels)
|
||||||
{
|
{
|
||||||
assert(length > 0);
|
assert(length > 0);
|
||||||
assert(lengthDiv8 * 8 == length);
|
assert(lengthDiv8 * 8 == length);
|
||||||
|
@ -272,7 +257,7 @@ uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSample
|
||||||
if (numChannels == 1)
|
if (numChannels == 1)
|
||||||
{
|
{
|
||||||
return evaluateFilterMono(dest, src, numSamples);
|
return evaluateFilterMono(dest, src, numSamples);
|
||||||
}
|
}
|
||||||
else if (numChannels == 2)
|
else if (numChannels == 2)
|
||||||
{
|
{
|
||||||
return evaluateFilterStereo(dest, src, numSamples);
|
return evaluateFilterStereo(dest, src, numSamples);
|
||||||
|
@ -286,9 +271,9 @@ uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSample
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||||
// depending on if we've a MMX-capable CPU available or not.
|
// depending on if we've a MMX-capable CPU available or not.
|
||||||
void * FIRFilter::operator new(size_t s)
|
void * FIRFilter::operator new(size_t)
|
||||||
{
|
{
|
||||||
// Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
|
// Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
|
||||||
ST_THROW_RT_ERROR("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!");
|
ST_THROW_RT_ERROR("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!");
|
||||||
|
@ -301,6 +286,7 @@ FIRFilter * FIRFilter::newInstance()
|
||||||
uint uExtensions;
|
uint uExtensions;
|
||||||
|
|
||||||
uExtensions = detectCPUextensions();
|
uExtensions = detectCPUextensions();
|
||||||
|
(void)uExtensions;
|
||||||
|
|
||||||
// Check if MMX/SSE instruction set extensions supported by CPU
|
// Check if MMX/SSE instruction set extensions supported by CPU
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// General FIR digital filter routines with MMX optimization.
|
/// General FIR digital filter routines with MMX optimization.
|
||||||
///
|
///
|
||||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
||||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -41,11 +41,11 @@
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
class FIRFilter
|
class FIRFilter
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
// Number of FIR filter taps
|
// Number of FIR filter taps
|
||||||
uint length;
|
uint length;
|
||||||
// Number of FIR filter taps divided by 8
|
// Number of FIR filter taps divided by 8
|
||||||
uint lengthDiv8;
|
uint lengthDiv8;
|
||||||
|
|
||||||
|
@ -59,11 +59,11 @@ protected:
|
||||||
SAMPLETYPE *filterCoeffs;
|
SAMPLETYPE *filterCoeffs;
|
||||||
SAMPLETYPE *filterCoeffsStereo;
|
SAMPLETYPE *filterCoeffsStereo;
|
||||||
|
|
||||||
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
uint numSamples) const;
|
uint numSamples) const;
|
||||||
virtual uint evaluateFilterMono(SAMPLETYPE *dest,
|
virtual uint evaluateFilterMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
uint numSamples) const;
|
uint numSamples) const;
|
||||||
virtual uint evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels);
|
virtual uint evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels);
|
||||||
|
|
||||||
|
@ -71,26 +71,26 @@ public:
|
||||||
FIRFilter();
|
FIRFilter();
|
||||||
virtual ~FIRFilter();
|
virtual ~FIRFilter();
|
||||||
|
|
||||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||||
/// depending on if we've a MMX-capable CPU available or not.
|
/// depending on if we've a MMX-capable CPU available or not.
|
||||||
static void * operator new(size_t s);
|
static void * operator new(size_t s);
|
||||||
|
|
||||||
static FIRFilter *newInstance();
|
static FIRFilter *newInstance();
|
||||||
|
|
||||||
/// Applies the filter to the given sequence of samples.
|
/// Applies the filter to the given sequence of samples.
|
||||||
/// Note : The amount of outputted samples is by value of 'filter_length'
|
/// Note : The amount of outputted samples is by value of 'filter_length'
|
||||||
/// smaller than the amount of input samples.
|
/// smaller than the amount of input samples.
|
||||||
///
|
///
|
||||||
/// \return Number of samples copied to 'dest'.
|
/// \return Number of samples copied to 'dest'.
|
||||||
uint evaluate(SAMPLETYPE *dest,
|
uint evaluate(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
uint numSamples,
|
uint numSamples,
|
||||||
uint numChannels);
|
uint numChannels);
|
||||||
|
|
||||||
uint getLength() const;
|
uint getLength() const;
|
||||||
|
|
||||||
virtual void setCoefficients(const SAMPLETYPE *coeffs,
|
virtual void setCoefficients(const SAMPLETYPE *coeffs,
|
||||||
uint newLength,
|
uint newLength,
|
||||||
uint uResultDivFactor);
|
uint uResultDivFactor);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Cubic interpolation routine.
|
/// Cubic interpolation routine.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
using namespace soundtouch;
|
using namespace soundtouch;
|
||||||
|
|
||||||
// cubic interpolation coefficients
|
// cubic interpolation coefficients
|
||||||
static const float _coeffs[]=
|
static const float _coeffs[]=
|
||||||
{ -0.5f, 1.0f, -0.5f, 0.0f,
|
{ -0.5f, 1.0f, -0.5f, 0.0f,
|
||||||
1.5f, -2.5f, 0.0f, 1.0f,
|
1.5f, -2.5f, 0.0f, 1.0f,
|
||||||
-1.5f, 2.0f, 0.5f, 0.0f,
|
-1.5f, 2.0f, 0.5f, 0.0f,
|
||||||
|
@ -56,10 +56,10 @@ void InterpolateCubic::resetRegisters()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Transpose mono audio. Returns number of produced output samples, and
|
/// Transpose mono audio. Returns number of produced output samples, and
|
||||||
/// updates "srcSamples" to amount of consumed source samples
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
int InterpolateCubic::transposeMono(SAMPLETYPE *pdest,
|
int InterpolateCubic::transposeMono(SAMPLETYPE *pdest,
|
||||||
const SAMPLETYPE *psrc,
|
const SAMPLETYPE *psrc,
|
||||||
int &srcSamples)
|
int &srcSamples)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -101,10 +101,10 @@ int InterpolateCubic::transposeMono(SAMPLETYPE *pdest,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Transpose stereo audio. Returns number of produced output samples, and
|
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||||
/// updates "srcSamples" to amount of consumed source samples
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
int InterpolateCubic::transposeStereo(SAMPLETYPE *pdest,
|
int InterpolateCubic::transposeStereo(SAMPLETYPE *pdest,
|
||||||
const SAMPLETYPE *psrc,
|
const SAMPLETYPE *psrc,
|
||||||
int &srcSamples)
|
int &srcSamples)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -148,10 +148,10 @@ int InterpolateCubic::transposeStereo(SAMPLETYPE *pdest,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Transpose multi-channel audio. Returns number of produced output samples, and
|
/// Transpose multi-channel audio. Returns number of produced output samples, and
|
||||||
/// updates "srcSamples" to amount of consumed source samples
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
int InterpolateCubic::transposeMulti(SAMPLETYPE *pdest,
|
int InterpolateCubic::transposeMulti(SAMPLETYPE *pdest,
|
||||||
const SAMPLETYPE *psrc,
|
const SAMPLETYPE *psrc,
|
||||||
int &srcSamples)
|
int &srcSamples)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Cubic interpolation routine.
|
/// Cubic interpolation routine.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -38,17 +38,17 @@
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
class InterpolateCubic final : public TransposerBase
|
class InterpolateCubic : public TransposerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) override;
|
int &srcSamples) override;
|
||||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) override;
|
int &srcSamples) override;
|
||||||
virtual int transposeMulti(SAMPLETYPE *dest,
|
virtual int transposeMulti(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) override;
|
int &srcSamples) override;
|
||||||
|
|
||||||
double fract;
|
double fract;
|
||||||
|
@ -58,7 +58,7 @@ public:
|
||||||
|
|
||||||
virtual void resetRegisters() override;
|
virtual void resetRegisters() override;
|
||||||
|
|
||||||
int getLatency() const override
|
virtual int getLatency() const override
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Linear interpolation algorithm.
|
/// Linear interpolation algorithm.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -38,7 +38,7 @@ using namespace soundtouch;
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// InterpolateLinearInteger - integer arithmetic implementation
|
// InterpolateLinearInteger - integer arithmetic implementation
|
||||||
//
|
//
|
||||||
|
|
||||||
/// fixed-point interpolation routine precision
|
/// fixed-point interpolation routine precision
|
||||||
#define SCALE 65536
|
#define SCALE 65536
|
||||||
|
@ -47,7 +47,7 @@ using namespace soundtouch;
|
||||||
// Constructor
|
// Constructor
|
||||||
InterpolateLinearInteger::InterpolateLinearInteger() : TransposerBase()
|
InterpolateLinearInteger::InterpolateLinearInteger() : TransposerBase()
|
||||||
{
|
{
|
||||||
// Notice: use local function calling syntax for sake of clarity,
|
// Notice: use local function calling syntax for sake of clarity,
|
||||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||||
resetRegisters();
|
resetRegisters();
|
||||||
setRate(1.0f);
|
setRate(1.0f);
|
||||||
|
@ -60,8 +60,8 @@ void InterpolateLinearInteger::resetRegisters()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||||
// the "dest" buffer
|
// the "dest" buffer
|
||||||
int InterpolateLinearInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
int InterpolateLinearInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
{
|
{
|
||||||
|
@ -73,7 +73,7 @@ int InterpolateLinearInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *
|
||||||
while (srcCount < srcSampleEnd)
|
while (srcCount < srcSampleEnd)
|
||||||
{
|
{
|
||||||
LONG_SAMPLETYPE temp;
|
LONG_SAMPLETYPE temp;
|
||||||
|
|
||||||
assert(iFract < SCALE);
|
assert(iFract < SCALE);
|
||||||
|
|
||||||
temp = (SCALE - iFract) * src[0] + iFract * src[1];
|
temp = (SCALE - iFract) * src[0] + iFract * src[1];
|
||||||
|
@ -93,8 +93,8 @@ int InterpolateLinearInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
// 'Stereo' version of the routine. Returns the number of samples returned in
|
// 'Stereo' version of the routine. Returns the number of samples returned in
|
||||||
// the "dest" buffer
|
// the "dest" buffer
|
||||||
int InterpolateLinearInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
int InterpolateLinearInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
{
|
{
|
||||||
|
@ -107,7 +107,7 @@ int InterpolateLinearInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE
|
||||||
{
|
{
|
||||||
LONG_SAMPLETYPE temp0;
|
LONG_SAMPLETYPE temp0;
|
||||||
LONG_SAMPLETYPE temp1;
|
LONG_SAMPLETYPE temp1;
|
||||||
|
|
||||||
assert(iFract < SCALE);
|
assert(iFract < SCALE);
|
||||||
|
|
||||||
temp0 = (SCALE - iFract) * src[0] + iFract * src[2];
|
temp0 = (SCALE - iFract) * src[0] + iFract * src[2];
|
||||||
|
@ -140,7 +140,7 @@ int InterpolateLinearInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE
|
||||||
while (srcCount < srcSampleEnd)
|
while (srcCount < srcSampleEnd)
|
||||||
{
|
{
|
||||||
LONG_SAMPLETYPE temp, vol1;
|
LONG_SAMPLETYPE temp, vol1;
|
||||||
|
|
||||||
assert(iFract < SCALE);
|
assert(iFract < SCALE);
|
||||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iFract);
|
vol1 = (LONG_SAMPLETYPE)(SCALE - iFract);
|
||||||
for (int c = 0; c < numChannels; c ++)
|
for (int c = 0; c < numChannels; c ++)
|
||||||
|
@ -164,7 +164,7 @@ int InterpolateLinearInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||||
// iRate, larger faster iRates.
|
// iRate, larger faster iRates.
|
||||||
void InterpolateLinearInteger::setRate(double newRate)
|
void InterpolateLinearInteger::setRate(double newRate)
|
||||||
{
|
{
|
||||||
|
@ -176,14 +176,14 @@ void InterpolateLinearInteger::setRate(double newRate)
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// InterpolateLinearFloat - floating point arithmetic implementation
|
// InterpolateLinearFloat - floating point arithmetic implementation
|
||||||
//
|
//
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
// Constructor
|
// Constructor
|
||||||
InterpolateLinearFloat::InterpolateLinearFloat() : TransposerBase()
|
InterpolateLinearFloat::InterpolateLinearFloat() : TransposerBase()
|
||||||
{
|
{
|
||||||
// Notice: use local function calling syntax for sake of clarity,
|
// Notice: use local function calling syntax for sake of clarity,
|
||||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||||
resetRegisters();
|
resetRegisters();
|
||||||
setRate(1.0);
|
setRate(1.0);
|
||||||
|
@ -196,8 +196,8 @@ void InterpolateLinearFloat::resetRegisters()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||||
// the "dest" buffer
|
// the "dest" buffer
|
||||||
int InterpolateLinearFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
int InterpolateLinearFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
{
|
{
|
||||||
|
@ -228,8 +228,8 @@ int InterpolateLinearFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *sr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||||
// the "dest" buffer
|
// the "dest" buffer
|
||||||
int InterpolateLinearFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
int InterpolateLinearFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||||
{
|
{
|
||||||
|
@ -272,7 +272,7 @@ int InterpolateLinearFloat::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *s
|
||||||
while (srcCount < srcSampleEnd)
|
while (srcCount < srcSampleEnd)
|
||||||
{
|
{
|
||||||
float temp, vol1, fract_float;
|
float temp, vol1, fract_float;
|
||||||
|
|
||||||
vol1 = (float)(1.0 - fract);
|
vol1 = (float)(1.0 - fract);
|
||||||
fract_float = (float)fract;
|
fract_float = (float)fract;
|
||||||
for (int c = 0; c < numChannels; c ++)
|
for (int c = 0; c < numChannels; c ++)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Linear interpolation routine.
|
/// Linear interpolation routine.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -39,29 +39,29 @@ namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
/// Linear transposer class that uses integer arithmetic
|
/// Linear transposer class that uses integer arithmetic
|
||||||
class InterpolateLinearInteger final : public TransposerBase
|
class InterpolateLinearInteger : public TransposerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
int iFract;
|
int iFract;
|
||||||
int iRate;
|
int iRate;
|
||||||
|
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) override;
|
int &srcSamples) override;
|
||||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) override;
|
int &srcSamples) override;
|
||||||
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) override;
|
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) override;
|
||||||
public:
|
public:
|
||||||
InterpolateLinearInteger();
|
InterpolateLinearInteger();
|
||||||
|
|
||||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||||
/// rate, larger faster rates.
|
/// rate, larger faster rates.
|
||||||
virtual void setRate(double newRate) override;
|
virtual void setRate(double newRate) override;
|
||||||
|
|
||||||
virtual void resetRegisters() override;
|
virtual void resetRegisters() override;
|
||||||
|
|
||||||
int getLatency() const override
|
virtual int getLatency() const override
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -69,25 +69,25 @@ public:
|
||||||
|
|
||||||
|
|
||||||
/// Linear transposer class that uses floating point arithmetic
|
/// Linear transposer class that uses floating point arithmetic
|
||||||
class InterpolateLinearFloat final : public TransposerBase
|
class InterpolateLinearFloat : public TransposerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
double fract;
|
double fract;
|
||||||
|
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) override;
|
int &srcSamples);
|
||||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) override;
|
int &srcSamples);
|
||||||
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) override;
|
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InterpolateLinearFloat();
|
InterpolateLinearFloat();
|
||||||
|
|
||||||
void resetRegisters() override;
|
virtual void resetRegisters();
|
||||||
|
|
||||||
int getLatency() const override
|
int getLatency() const
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Sample interpolation routine using 8-tap band-limited Shannon interpolation
|
/// Sample interpolation routine using 8-tap band-limited Shannon interpolation
|
||||||
/// with kaiser window.
|
/// with kaiser window.
|
||||||
///
|
///
|
||||||
/// Notice. This algorithm is remarkably much heavier than linear or cubic
|
/// Notice. This algorithm is remarkably much heavier than linear or cubic
|
||||||
|
@ -43,7 +43,7 @@ using namespace soundtouch;
|
||||||
|
|
||||||
/// Kaiser window with beta = 2.0
|
/// Kaiser window with beta = 2.0
|
||||||
/// Values scaled down by 5% to avoid overflows
|
/// Values scaled down by 5% to avoid overflows
|
||||||
static const double _kaiser8[8] =
|
static const double _kaiser8[8] =
|
||||||
{
|
{
|
||||||
0.41778693317814,
|
0.41778693317814,
|
||||||
0.64888025049173,
|
0.64888025049173,
|
||||||
|
@ -71,10 +71,10 @@ void InterpolateShannon::resetRegisters()
|
||||||
#define PI 3.1415926536
|
#define PI 3.1415926536
|
||||||
#define sinc(x) (sin(PI * (x)) / (PI * (x)))
|
#define sinc(x) (sin(PI * (x)) / (PI * (x)))
|
||||||
|
|
||||||
/// Transpose mono audio. Returns number of produced output samples, and
|
/// Transpose mono audio. Returns number of produced output samples, and
|
||||||
/// updates "srcSamples" to amount of consumed source samples
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
int InterpolateShannon::transposeMono(SAMPLETYPE *pdest,
|
int InterpolateShannon::transposeMono(SAMPLETYPE *pdest,
|
||||||
const SAMPLETYPE *psrc,
|
const SAMPLETYPE *psrc,
|
||||||
int &srcSamples)
|
int &srcSamples)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -119,10 +119,10 @@ int InterpolateShannon::transposeMono(SAMPLETYPE *pdest,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Transpose stereo audio. Returns number of produced output samples, and
|
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||||
/// updates "srcSamples" to amount of consumed source samples
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
int InterpolateShannon::transposeStereo(SAMPLETYPE *pdest,
|
int InterpolateShannon::transposeStereo(SAMPLETYPE *pdest,
|
||||||
const SAMPLETYPE *psrc,
|
const SAMPLETYPE *psrc,
|
||||||
int &srcSamples)
|
int &srcSamples)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -169,11 +169,11 @@ int InterpolateShannon::transposeStereo(SAMPLETYPE *pdest,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Transpose stereo audio. Returns number of produced output samples, and
|
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||||
/// updates "srcSamples" to amount of consumed source samples
|
/// updates "srcSamples" to amount of consumed source samples
|
||||||
int InterpolateShannon::transposeMulti(SAMPLETYPE *pdest,
|
int InterpolateShannon::transposeMulti(SAMPLETYPE *,
|
||||||
const SAMPLETYPE *psrc,
|
const SAMPLETYPE *,
|
||||||
int &srcSamples)
|
int &)
|
||||||
{
|
{
|
||||||
// not implemented
|
// not implemented
|
||||||
assert(false);
|
assert(false);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Sample interpolation routine using 8-tap band-limited Shannon interpolation
|
/// Sample interpolation routine using 8-tap band-limited Shannon interpolation
|
||||||
/// with kaiser window.
|
/// with kaiser window.
|
||||||
///
|
///
|
||||||
/// Notice. This algorithm is remarkably much heavier than linear or cubic
|
/// Notice. This algorithm is remarkably much heavier than linear or cubic
|
||||||
|
@ -43,17 +43,17 @@
|
||||||
namespace soundtouch
|
namespace soundtouch
|
||||||
{
|
{
|
||||||
|
|
||||||
class InterpolateShannon final : public TransposerBase
|
class InterpolateShannon : public TransposerBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
int transposeMono(SAMPLETYPE *dest,
|
int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) override;
|
int &srcSamples) override;
|
||||||
int transposeStereo(SAMPLETYPE *dest,
|
int transposeStereo(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) override;
|
int &srcSamples) override;
|
||||||
int transposeMulti(SAMPLETYPE *dest,
|
int transposeMulti(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) override;
|
int &srcSamples) override;
|
||||||
|
|
||||||
double fract;
|
double fract;
|
||||||
|
@ -63,7 +63,7 @@ public:
|
||||||
|
|
||||||
void resetRegisters() override;
|
void resetRegisters() override;
|
||||||
|
|
||||||
int getLatency() const override
|
virtual int getLatency() const override
|
||||||
{
|
{
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Peak detection routine.
|
/// Peak detection routine.
|
||||||
///
|
///
|
||||||
/// The routine detects highest value on an array of values and calculates the
|
/// The routine detects highest value on an array of values and calculates the
|
||||||
/// precise peak location as a mass-center of the 'hump' around the peak value.
|
/// precise peak location as a mass-center of the 'hump' around the peak value.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -80,7 +80,7 @@ int PeakFinder::findTop(const float *data, int peakpos) const
|
||||||
|
|
||||||
|
|
||||||
// Finds 'ground level' of a peak hump by starting from 'peakpos' and proceeding
|
// Finds 'ground level' of a peak hump by starting from 'peakpos' and proceeding
|
||||||
// to direction defined by 'direction' until next 'hump' after minimum value will
|
// to direction defined by 'direction' until next 'hump' after minimum value will
|
||||||
// begin
|
// begin
|
||||||
int PeakFinder::findGround(const float *data, int peakpos, int direction) const
|
int PeakFinder::findGround(const float *data, int peakpos, int direction) const
|
||||||
{
|
{
|
||||||
|
@ -186,7 +186,7 @@ double PeakFinder::getPeakCenter(const float *data, int peakpos) const
|
||||||
|
|
||||||
peakLevel = data[peakpos];
|
peakLevel = data[peakpos];
|
||||||
|
|
||||||
if (gp1 == gp2)
|
if (gp1 == gp2)
|
||||||
{
|
{
|
||||||
// avoid rounding errors when all are equal
|
// avoid rounding errors when all are equal
|
||||||
assert(gp1 == peakpos);
|
assert(gp1 == peakpos);
|
||||||
|
@ -210,7 +210,7 @@ double PeakFinder::getPeakCenter(const float *data, int peakpos) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
||||||
{
|
{
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
@ -225,19 +225,19 @@ double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
||||||
peak = data[minPos];
|
peak = data[minPos];
|
||||||
for (i = minPos + 1; i < maxPos; i ++)
|
for (i = minPos + 1; i < maxPos; i ++)
|
||||||
{
|
{
|
||||||
if (data[i] > peak)
|
if (data[i] > peak)
|
||||||
{
|
{
|
||||||
peak = data[i];
|
peak = data[i];
|
||||||
peakpos = i;
|
peakpos = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate exact location of the highest peak mass center
|
// Calculate exact location of the highest peak mass center
|
||||||
highPeak = getPeakCenter(data, peakpos);
|
highPeak = getPeakCenter(data, peakpos);
|
||||||
peak = highPeak;
|
peak = highPeak;
|
||||||
|
|
||||||
// Now check if the highest peak were in fact harmonic of the true base beat peak
|
// Now check if the highest peak were in fact harmonic of the true base beat peak
|
||||||
// - sometimes the highest peak can be Nth harmonic of the true base peak yet
|
// - sometimes the highest peak can be Nth harmonic of the true base peak yet
|
||||||
// just a slightly higher than the true base
|
// just a slightly higher than the true base
|
||||||
|
|
||||||
for (i = 1; i < 3; i ++)
|
for (i = 1; i < 3; i ++)
|
||||||
|
@ -254,7 +254,7 @@ double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
||||||
// calculate mass-center of possible harmonic peak
|
// calculate mass-center of possible harmonic peak
|
||||||
peaktmp = getPeakCenter(data, peakpos);
|
peaktmp = getPeakCenter(data, peakpos);
|
||||||
|
|
||||||
// accept harmonic peak if
|
// accept harmonic peak if
|
||||||
// (a) it is found
|
// (a) it is found
|
||||||
// (b) is within ±4% of the expected harmonic interval
|
// (b) is within ±4% of the expected harmonic interval
|
||||||
// (c) has at least half x-corr value of the max. peak
|
// (c) has at least half x-corr value of the max. peak
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// The routine detects highest value on an array of values and calculates the
|
/// The routine detects highest value on an array of values and calculates the
|
||||||
/// precise peak location as a mass-center of the 'hump' around the peak value.
|
/// precise peak location as a mass-center of the 'hump' around the peak value.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -60,7 +60,7 @@ protected:
|
||||||
int findTop(const float *data, int peakpos) const;
|
int findTop(const float *data, int peakpos) const;
|
||||||
|
|
||||||
|
|
||||||
/// Finds the 'ground' level, i.e. smallest level between two neighbouring peaks, to right-
|
/// Finds the 'ground' level, i.e. smallest level between two neighbouring peaks, to right-
|
||||||
/// or left-hand side of the given peak position.
|
/// or left-hand side of the given peak position.
|
||||||
int findGround(const float *data, /// Data vector.
|
int findGround(const float *data, /// Data vector.
|
||||||
int peakpos, /// Peak position index within the data vector.
|
int peakpos, /// Peak position index within the data vector.
|
||||||
|
@ -71,7 +71,7 @@ protected:
|
||||||
double getPeakCenter(const float *data, int peakpos) const;
|
double getPeakCenter(const float *data, int peakpos) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
PeakFinder();
|
PeakFinder();
|
||||||
|
|
||||||
/// Detect exact peak position of the data vector by finding the largest peak 'hump'
|
/// Detect exact peak position of the data vector by finding the largest peak 'hump'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Sample rate transposer. Changes sample rate by using linear interpolation
|
/// Sample rate transposer. Changes sample rate by using linear interpolation
|
||||||
/// together with anti-alias filtering (first order interpolation with anti-
|
/// together with anti-alias filtering (first order interpolation with anti-
|
||||||
/// alias filtering should be quite adequate for this application)
|
/// alias filtering should be quite adequate for this application)
|
||||||
///
|
///
|
||||||
|
@ -50,7 +50,7 @@ TransposerBase::ALGORITHM TransposerBase::algorithm = TransposerBase::CUBIC;
|
||||||
// Constructor
|
// Constructor
|
||||||
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
||||||
{
|
{
|
||||||
bUseAAFilter =
|
bUseAAFilter =
|
||||||
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
true;
|
true;
|
||||||
#else
|
#else
|
||||||
|
@ -96,7 +96,7 @@ AAFilter *RateTransposer::getAAFilter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||||
// iRate, larger faster iRates.
|
// iRate, larger faster iRates.
|
||||||
void RateTransposer::setRate(double newRate)
|
void RateTransposer::setRate(double newRate)
|
||||||
{
|
{
|
||||||
|
@ -105,11 +105,11 @@ void RateTransposer::setRate(double newRate)
|
||||||
pTransposer->setRate(newRate);
|
pTransposer->setRate(newRate);
|
||||||
|
|
||||||
// design a new anti-alias filter
|
// design a new anti-alias filter
|
||||||
if (newRate > 1.0)
|
if (newRate > 1.0)
|
||||||
{
|
{
|
||||||
fCutoff = 0.5 / newRate;
|
fCutoff = 0.5 / newRate;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fCutoff = 0.5 * newRate;
|
fCutoff = 0.5 * newRate;
|
||||||
}
|
}
|
||||||
|
@ -125,14 +125,12 @@ void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Transposes sample rate by applying anti-alias filter to prevent folding.
|
// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||||
// Returns amount of samples returned in the "dest" buffer.
|
// 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 maximum amount of samples that can be returned at a time is set by
|
||||||
// the 'set_returnBuffer_size' function.
|
// the 'set_returnBuffer_size' function.
|
||||||
void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
||||||
{
|
{
|
||||||
uint count;
|
|
||||||
|
|
||||||
if (nSamples == 0) return;
|
if (nSamples == 0) return;
|
||||||
|
|
||||||
// Store samples to input buffer
|
// Store samples to input buffer
|
||||||
|
@ -140,16 +138,16 @@ void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
||||||
|
|
||||||
// If anti-alias filter is turned off, simply transpose without applying
|
// If anti-alias filter is turned off, simply transpose without applying
|
||||||
// the filter
|
// the filter
|
||||||
if (bUseAAFilter == false)
|
if (bUseAAFilter == false)
|
||||||
{
|
{
|
||||||
count = pTransposer->transpose(outputBuffer, inputBuffer);
|
(void)pTransposer->transpose(outputBuffer, inputBuffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(pAAFilter);
|
assert(pAAFilter);
|
||||||
|
|
||||||
// Transpose with anti-alias filter
|
// Transpose with anti-alias filter
|
||||||
if (pTransposer->rate < 1.0f)
|
if (pTransposer->rate < 1.0f)
|
||||||
{
|
{
|
||||||
// If the parameter 'Rate' value is smaller than 1, first transpose
|
// If the parameter 'Rate' value is smaller than 1, first transpose
|
||||||
// the samples and then apply the anti-alias filter to remove aliasing.
|
// the samples and then apply the anti-alias filter to remove aliasing.
|
||||||
|
@ -159,8 +157,8 @@ void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
||||||
|
|
||||||
// Apply the anti-alias filter for transposed samples in midBuffer
|
// Apply the anti-alias filter for transposed samples in midBuffer
|
||||||
pAAFilter->evaluate(outputBuffer, midBuffer);
|
pAAFilter->evaluate(outputBuffer, midBuffer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If the parameter 'Rate' value is larger than 1, first apply the
|
// If the parameter 'Rate' value is larger than 1, first apply the
|
||||||
// anti-alias filter to remove high frequencies (prevent them from folding
|
// anti-alias filter to remove high frequencies (prevent them from folding
|
||||||
|
@ -224,7 +222,7 @@ int RateTransposer::getLatency() const
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// TransposerBase - Base class for interpolation
|
// TransposerBase - Base class for interpolation
|
||||||
//
|
//
|
||||||
|
|
||||||
// static function to set interpolation algorithm
|
// static function to set interpolation algorithm
|
||||||
void TransposerBase::setAlgorithm(TransposerBase::ALGORITHM a)
|
void TransposerBase::setAlgorithm(TransposerBase::ALGORITHM a)
|
||||||
|
@ -233,7 +231,7 @@ void TransposerBase::setAlgorithm(TransposerBase::ALGORITHM a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Transposes the sample rate of the given samples using linear interpolation.
|
// Transposes the sample rate of the given samples using linear interpolation.
|
||||||
// Returns the number of samples returned in the "dest" buffer
|
// Returns the number of samples returned in the "dest" buffer
|
||||||
int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
|
int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
|
||||||
{
|
{
|
||||||
|
@ -248,11 +246,11 @@ int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
|
||||||
{
|
{
|
||||||
numOutput = transposeMono(pdest, psrc, numSrcSamples);
|
numOutput = transposeMono(pdest, psrc, numSrcSamples);
|
||||||
}
|
}
|
||||||
else if (numChannels == 2)
|
else if (numChannels == 2)
|
||||||
{
|
{
|
||||||
numOutput = transposeStereo(pdest, psrc, numSrcSamples);
|
numOutput = transposeStereo(pdest, psrc, numSrcSamples);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif // USE_MULTICH_ALWAYS
|
#endif // USE_MULTICH_ALWAYS
|
||||||
{
|
{
|
||||||
assert(numChannels > 0);
|
assert(numChannels > 0);
|
||||||
|
@ -309,7 +307,7 @@ TransposerBase *TransposerBase::newInstance()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Sample rate transposer. Changes sample rate by using linear interpolation
|
/// Sample rate transposer. Changes sample rate by using linear interpolation
|
||||||
/// together with anti-alias filtering (first order interpolation with anti-
|
/// together with anti-alias filtering (first order interpolation with anti-
|
||||||
/// alias filtering should be quite adequate for this application).
|
/// alias filtering should be quite adequate for this application).
|
||||||
///
|
///
|
||||||
/// Use either of the derived classes of 'RateTransposerInteger' or
|
/// Use either of the derived classes of 'RateTransposerInteger' or
|
||||||
/// 'RateTransposerFloat' for corresponding integer/floating point tranposing
|
/// 'RateTransposerFloat' for corresponding integer/floating point tranposing
|
||||||
/// algorithm implementation.
|
/// algorithm implementation.
|
||||||
///
|
///
|
||||||
|
@ -59,14 +59,14 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual int transposeMono(SAMPLETYPE *dest,
|
virtual int transposeMono(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) = 0;
|
int &srcSamples) = 0;
|
||||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) = 0;
|
int &srcSamples) = 0;
|
||||||
virtual int transposeMulti(SAMPLETYPE *dest,
|
virtual int transposeMulti(SAMPLETYPE *dest,
|
||||||
const SAMPLETYPE *src,
|
const SAMPLETYPE *src,
|
||||||
int &srcSamples) = 0;
|
int &srcSamples) = 0;
|
||||||
|
|
||||||
static ALGORITHM algorithm;
|
static ALGORITHM algorithm;
|
||||||
|
@ -115,11 +115,11 @@ protected:
|
||||||
bool bUseAAFilter;
|
bool bUseAAFilter;
|
||||||
|
|
||||||
|
|
||||||
/// Transposes sample rate by applying anti-alias filter to prevent folding.
|
/// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||||
/// Returns amount of samples returned in the "dest" buffer.
|
/// 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 maximum amount of samples that can be returned at a time is set by
|
||||||
/// the 'set_returnBuffer_size' function.
|
/// the 'set_returnBuffer_size' function.
|
||||||
void processSamples(const SAMPLETYPE *src,
|
void processSamples(const SAMPLETYPE *src,
|
||||||
uint numSamples);
|
uint numSamples);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -138,7 +138,7 @@ public:
|
||||||
/// Returns nonzero if anti-alias filter is enabled.
|
/// Returns nonzero if anti-alias filter is enabled.
|
||||||
bool isAAFilterEnabled() const;
|
bool isAAFilterEnabled() const;
|
||||||
|
|
||||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||||
/// rate, larger faster rates.
|
/// rate, larger faster rates.
|
||||||
virtual void setRate(double newRate);
|
virtual void setRate(double newRate);
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||||
///
|
///
|
||||||
/// Notes:
|
/// Notes:
|
||||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||||
/// desired tempo/pitch/rate settings with the corresponding functions.
|
/// desired tempo/pitch/rate settings with the corresponding functions.
|
||||||
///
|
///
|
||||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||||
/// samples that are to be processed are fed into one of the pipe by calling
|
/// samples that are to be processed are fed into one of the pipe by calling
|
||||||
/// function 'putSamples', while the ready processed samples can be read
|
/// function 'putSamples', while the ready processed samples can be read
|
||||||
/// from the other end of the pipeline with function 'receiveSamples'.
|
/// from the other end of the pipeline with function 'receiveSamples'.
|
||||||
///
|
///
|
||||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||||
/// samples in order to process the sound. For this reason the classes buffer
|
/// samples in order to process the sound. For this reason the classes buffer
|
||||||
/// incoming samples until there are enough of samples available for
|
/// incoming samples until there are enough of samples available for
|
||||||
/// processing, then they carry out the processing step and consequently
|
/// processing, then they carry out the processing step and consequently
|
||||||
/// make the processed samples available for outputting.
|
/// make the processed samples available for outputting.
|
||||||
///
|
///
|
||||||
/// - For the above reason, the processing routines introduce a certain
|
/// - For the above reason, the processing routines introduce a certain
|
||||||
/// 'latency' between the input and output, so that the samples input to
|
/// 'latency' between the input and output, so that the samples input to
|
||||||
/// SoundTouch may not be immediately available in the output, and neither
|
/// SoundTouch may not be immediately available in the output, and neither
|
||||||
/// the amount of outputtable samples may not immediately be in direct
|
/// the amount of outputtable samples may not immediately be in direct
|
||||||
/// relationship with the amount of previously input samples.
|
/// relationship with the amount of previously input samples.
|
||||||
///
|
///
|
||||||
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
||||||
|
@ -30,8 +30,8 @@
|
||||||
/// required.
|
/// required.
|
||||||
///
|
///
|
||||||
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
||||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||||
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
||||||
/// combining the two other controls.
|
/// combining the two other controls.
|
||||||
///
|
///
|
||||||
|
@ -74,7 +74,7 @@
|
||||||
#include "cpu_detect.h"
|
#include "cpu_detect.h"
|
||||||
|
|
||||||
using namespace soundtouch;
|
using namespace soundtouch;
|
||||||
|
|
||||||
/// test if two floating point numbers are equal
|
/// test if two floating point numbers are equal
|
||||||
#define TEST_FLOAT_EQUAL(a, b) (fabs(a - b) < 1e-10)
|
#define TEST_FLOAT_EQUAL(a, b) (fabs(a - b) < 1e-10)
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ using namespace soundtouch;
|
||||||
extern "C" void soundtouch_ac_test()
|
extern "C" void soundtouch_ac_test()
|
||||||
{
|
{
|
||||||
printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
|
printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SoundTouch::SoundTouch()
|
SoundTouch::SoundTouch()
|
||||||
|
@ -97,8 +97,8 @@ SoundTouch::SoundTouch()
|
||||||
|
|
||||||
rate = tempo = 0;
|
rate = tempo = 0;
|
||||||
|
|
||||||
virtualPitch =
|
virtualPitch =
|
||||||
virtualRate =
|
virtualRate =
|
||||||
virtualTempo = 1.0;
|
virtualTempo = 1.0;
|
||||||
|
|
||||||
calcEffectiveRateAndTempo();
|
calcEffectiveRateAndTempo();
|
||||||
|
@ -227,9 +227,9 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
||||||
if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo);
|
if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo);
|
||||||
|
|
||||||
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
if (rate <= 1.0f)
|
if (rate <= 1.0f)
|
||||||
{
|
{
|
||||||
if (output != pTDStretch)
|
if (output != pTDStretch)
|
||||||
{
|
{
|
||||||
FIFOSamplePipe *tempoOut;
|
FIFOSamplePipe *tempoOut;
|
||||||
|
|
||||||
|
@ -246,7 +246,7 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (output != pRateTransposer)
|
if (output != pRateTransposer)
|
||||||
{
|
{
|
||||||
FIFOSamplePipe *transOut;
|
FIFOSamplePipe *transOut;
|
||||||
|
|
||||||
|
@ -259,7 +259,7 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
||||||
|
|
||||||
output = pRateTransposer;
|
output = pRateTransposer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -276,31 +276,31 @@ void SoundTouch::setSampleRate(uint srate)
|
||||||
// the input of the object.
|
// the input of the object.
|
||||||
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||||
{
|
{
|
||||||
if (bSrateSet == false)
|
if (bSrateSet == false)
|
||||||
{
|
{
|
||||||
ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined");
|
ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined");
|
||||||
}
|
}
|
||||||
else if (channels == 0)
|
else if (channels == 0)
|
||||||
{
|
{
|
||||||
ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
|
ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
// accumulate how many samples are expected out from processing, given the current
|
// accumulate how many samples are expected out from processing, given the current
|
||||||
// processing setting
|
// processing setting
|
||||||
samplesExpectedOut += (double)nSamples / ((double)rate * (double)tempo);
|
samplesExpectedOut += (double)nSamples / ((double)rate * (double)tempo);
|
||||||
|
|
||||||
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||||
if (rate <= 1.0f)
|
if (rate <= 1.0f)
|
||||||
{
|
{
|
||||||
// transpose the rate down, output the transposed sound to tempo changer buffer
|
// transpose the rate down, output the transposed sound to tempo changer buffer
|
||||||
assert(output == pTDStretch);
|
assert(output == pTDStretch);
|
||||||
pRateTransposer->putSamples(samples, nSamples);
|
pRateTransposer->putSamples(samples, nSamples);
|
||||||
pTDStretch->moveSamples(*pRateTransposer);
|
pTDStretch->moveSamples(*pRateTransposer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
// evaluate the tempo changer, then transpose the rate up,
|
// evaluate the tempo changer, then transpose the rate up,
|
||||||
assert(output == pRateTransposer);
|
assert(output == pRateTransposer);
|
||||||
pTDStretch->putSamples(samples, nSamples);
|
pTDStretch->putSamples(samples, nSamples);
|
||||||
pRateTransposer->moveSamples(*pTDStretch);
|
pRateTransposer->moveSamples(*pTDStretch);
|
||||||
|
@ -327,8 +327,8 @@ void SoundTouch::flush()
|
||||||
|
|
||||||
memset(buff, 0, 128 * channels * sizeof(SAMPLETYPE));
|
memset(buff, 0, 128 * channels * sizeof(SAMPLETYPE));
|
||||||
// "Push" the last active samples out from the processing pipeline by
|
// "Push" the last active samples out from the processing pipeline by
|
||||||
// feeding blank samples into the processing pipeline until new,
|
// feeding blank samples into the processing pipeline until new,
|
||||||
// processed samples appear in the output (not however, more than
|
// processed samples appear in the output (not however, more than
|
||||||
// 24ksamples in any case)
|
// 24ksamples in any case)
|
||||||
for (i = 0; (numStillExpected > (int)numSamples()) && (i < 200); i ++)
|
for (i = 0; (numStillExpected > (int)numSamples()) && (i < 200); i ++)
|
||||||
{
|
{
|
||||||
|
@ -355,7 +355,7 @@ bool SoundTouch::setSetting(int settingId, int value)
|
||||||
// read current tdstretch routine parameters
|
// read current tdstretch routine parameters
|
||||||
pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
|
pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
|
||||||
|
|
||||||
switch (settingId)
|
switch (settingId)
|
||||||
{
|
{
|
||||||
case SETTING_USE_AA_FILTER :
|
case SETTING_USE_AA_FILTER :
|
||||||
// enables / disabless anti-alias filter
|
// enables / disabless anti-alias filter
|
||||||
|
@ -401,7 +401,7 @@ int SoundTouch::getSetting(int settingId) const
|
||||||
{
|
{
|
||||||
int temp;
|
int temp;
|
||||||
|
|
||||||
switch (settingId)
|
switch (settingId)
|
||||||
{
|
{
|
||||||
case SETTING_USE_AA_FILTER :
|
case SETTING_USE_AA_FILTER :
|
||||||
return (uint)pRateTransposer->isAAFilterEnabled();
|
return (uint)pRateTransposer->isAAFilterEnabled();
|
||||||
|
@ -413,15 +413,15 @@ int SoundTouch::getSetting(int settingId) const
|
||||||
return (uint)pTDStretch->isQuickSeekEnabled();
|
return (uint)pTDStretch->isQuickSeekEnabled();
|
||||||
|
|
||||||
case SETTING_SEQUENCE_MS:
|
case SETTING_SEQUENCE_MS:
|
||||||
pTDStretch->getParameters(NULL, &temp, NULL, NULL);
|
pTDStretch->getParameters(nullptr, &temp, nullptr, nullptr);
|
||||||
return temp;
|
return temp;
|
||||||
|
|
||||||
case SETTING_SEEKWINDOW_MS:
|
case SETTING_SEEKWINDOW_MS:
|
||||||
pTDStretch->getParameters(NULL, NULL, &temp, NULL);
|
pTDStretch->getParameters(nullptr, nullptr, &temp, nullptr);
|
||||||
return temp;
|
return temp;
|
||||||
|
|
||||||
case SETTING_OVERLAP_MS:
|
case SETTING_OVERLAP_MS:
|
||||||
pTDStretch->getParameters(NULL, NULL, NULL, &temp);
|
pTDStretch->getParameters(nullptr, nullptr, nullptr, &temp);
|
||||||
return temp;
|
return temp;
|
||||||
|
|
||||||
case SETTING_NOMINAL_INPUT_SEQUENCE :
|
case SETTING_NOMINAL_INPUT_SEQUENCE :
|
||||||
|
@ -503,8 +503,8 @@ uint SoundTouch::numUnprocessedSamples() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||||
/// output buffer and removes them from the sample buffer. If there are less than
|
/// output buffer and removes them from the sample buffer. If there are less than
|
||||||
/// 'numsample' samples in the buffer, returns all that available.
|
/// 'numsample' samples in the buffer, returns all that available.
|
||||||
///
|
///
|
||||||
/// \return Number of samples returned.
|
/// \return Number of samples returned.
|
||||||
|
@ -516,8 +516,8 @@ uint SoundTouch::receiveSamples(SAMPLETYPE *output, uint maxSamples)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||||
/// sample buffer without copying them anywhere.
|
/// sample buffer without copying them anywhere.
|
||||||
///
|
///
|
||||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||||
/// with 'ptrBegin' function.
|
/// with 'ptrBegin' function.
|
||||||
|
@ -530,7 +530,7 @@ uint SoundTouch::receiveSamples(uint maxSamples)
|
||||||
|
|
||||||
|
|
||||||
/// Get ratio between input and output audio durations, useful for calculating
|
/// Get ratio between input and output audio durations, useful for calculating
|
||||||
/// processed output duration: if you'll process a stream of N samples, then
|
/// processed output duration: if you'll process a stream of N samples, then
|
||||||
/// you can expect to get out N * getInputOutputSampleRatio() samples.
|
/// you can expect to get out N * getInputOutputSampleRatio() samples.
|
||||||
double SoundTouch::getInputOutputSampleRatio()
|
double SoundTouch::getInputOutputSampleRatio()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||||
/// while maintaining the original pitch by using a time domain WSOLA-like
|
/// while maintaining the original pitch by using a time domain WSOLA-like
|
||||||
/// method with several performance-increasing tweaks.
|
/// method with several performance-increasing tweaks.
|
||||||
///
|
///
|
||||||
/// Notes : MMX optimized functions reside in a separate, platform-specific
|
/// Notes : MMX optimized functions reside in a separate, platform-specific
|
||||||
/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'.
|
/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'.
|
||||||
///
|
///
|
||||||
/// This source file contains OpenMP optimizations that allow speeding up the
|
/// This source file contains OpenMP optimizations that allow speeding up the
|
||||||
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
||||||
/// in parallel. See the following article link for more detailed discussion
|
/// in parallel. See the following article link for more detailed discussion
|
||||||
/// about SoundTouch OpenMP optimizations:
|
/// about SoundTouch OpenMP optimizations:
|
||||||
/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices
|
/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices
|
||||||
///
|
///
|
||||||
|
@ -54,25 +54,6 @@ using namespace soundtouch;
|
||||||
|
|
||||||
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
||||||
|
|
||||||
/*****************************************************************************
|
|
||||||
*
|
|
||||||
* Constant definitions
|
|
||||||
*
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
// Table for the hierarchical mixing position seeking algorithm
|
|
||||||
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},
|
|
||||||
{ 121, 114, 97, 114, 98, 105, 108, 32, 104, 99, 117, 111,
|
|
||||||
116, 100, 110, 117, 111, 115, 0, 0, 0, 0, 0, 0}};
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
* Implementation of the class 'TDStretch'
|
* Implementation of the class 'TDStretch'
|
||||||
|
@ -85,8 +66,8 @@ TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
||||||
bQuickSeek = false;
|
bQuickSeek = false;
|
||||||
channels = 2;
|
channels = 2;
|
||||||
|
|
||||||
pMidBuffer = NULL;
|
pMidBuffer = nullptr;
|
||||||
pMidBufferUnaligned = NULL;
|
pMidBufferUnaligned = nullptr;
|
||||||
overlapLength = 0;
|
overlapLength = 0;
|
||||||
|
|
||||||
bAutoSeqSetting = true;
|
bAutoSeqSetting = true;
|
||||||
|
@ -113,11 +94,11 @@ TDStretch::~TDStretch()
|
||||||
//
|
//
|
||||||
// 'sampleRate' = sample rate of the sound
|
// 'sampleRate' = sample rate of the sound
|
||||||
// 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms)
|
// 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms)
|
||||||
// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||||
// position (default = 28 ms)
|
// position (default = 28 ms)
|
||||||
// 'overlapMS' = overlapping length (default = 12 ms)
|
// 'overlapMS' = overlapping length (default = 12 ms)
|
||||||
|
|
||||||
void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
||||||
int aSeekWindowMS, int aOverlapMS)
|
int aSeekWindowMS, int aOverlapMS)
|
||||||
{
|
{
|
||||||
// accept only positive parameter values - if zero or negative, use old values instead
|
// accept only positive parameter values - if zero or negative, use old values instead
|
||||||
|
@ -133,19 +114,19 @@ void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
||||||
{
|
{
|
||||||
this->sequenceMs = aSequenceMS;
|
this->sequenceMs = aSequenceMS;
|
||||||
bAutoSeqSetting = false;
|
bAutoSeqSetting = false;
|
||||||
}
|
}
|
||||||
else if (aSequenceMS == 0)
|
else if (aSequenceMS == 0)
|
||||||
{
|
{
|
||||||
// if zero, use automatic setting
|
// if zero, use automatic setting
|
||||||
bAutoSeqSetting = true;
|
bAutoSeqSetting = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aSeekWindowMS > 0)
|
if (aSeekWindowMS > 0)
|
||||||
{
|
{
|
||||||
this->seekWindowMs = aSeekWindowMS;
|
this->seekWindowMs = aSeekWindowMS;
|
||||||
bAutoSeekSetting = false;
|
bAutoSeekSetting = false;
|
||||||
}
|
}
|
||||||
else if (aSeekWindowMS == 0)
|
else if (aSeekWindowMS == 0)
|
||||||
{
|
{
|
||||||
// if zero, use automatic setting
|
// if zero, use automatic setting
|
||||||
bAutoSeekSetting = true;
|
bAutoSeekSetting = true;
|
||||||
|
@ -162,7 +143,7 @@ void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
||||||
|
|
||||||
|
|
||||||
/// Get routine control parameters, see setParameters() function.
|
/// Get routine control parameters, see setParameters() function.
|
||||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
/// Any of the parameters to this function can be nullptr, in such case corresponding parameter
|
||||||
/// value isn't returned.
|
/// value isn't returned.
|
||||||
void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const
|
void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const
|
||||||
{
|
{
|
||||||
|
@ -251,11 +232,11 @@ bool TDStretch::isQuickSeekEnabled() const
|
||||||
// Seeks for the optimal overlap-mixing position.
|
// Seeks for the optimal overlap-mixing position.
|
||||||
int TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
|
int TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
|
||||||
{
|
{
|
||||||
if (bQuickSeek)
|
if (bQuickSeek)
|
||||||
{
|
{
|
||||||
return seekBestOverlapPositionQuick(refPos);
|
return seekBestOverlapPositionQuick(refPos);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return seekBestOverlapPositionFull(refPos);
|
return seekBestOverlapPositionFull(refPos);
|
||||||
}
|
}
|
||||||
|
@ -276,8 +257,8 @@ inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, ui
|
||||||
{
|
{
|
||||||
// stereo sound
|
// stereo sound
|
||||||
overlapStereo(pOutput, pInput + 2 * ovlPos);
|
overlapStereo(pOutput, pInput + 2 * ovlPos);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif // USE_MULTICH_ALWAYS
|
#endif // USE_MULTICH_ALWAYS
|
||||||
{
|
{
|
||||||
assert(channels > 0);
|
assert(channels > 0);
|
||||||
|
@ -292,7 +273,7 @@ inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, ui
|
||||||
// The best position is determined as the position where the two overlapped
|
// The best position is determined as the position where the two overlapped
|
||||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||||
// value over the overlapping period
|
// value over the overlapping period
|
||||||
int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
{
|
{
|
||||||
int bestOffs;
|
int bestOffs;
|
||||||
double bestCorr;
|
double bestCorr;
|
||||||
|
@ -319,7 +300,7 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
corr = calcCrossCorr(refPos + channels * i, pMidBuffer, norm);
|
corr = calcCrossCorr(refPos + channels * i, pMidBuffer, norm);
|
||||||
#else
|
#else
|
||||||
// In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same
|
// In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same
|
||||||
// as "calcCrossCorr", but saves time by reusing & updating previously stored
|
// as "calcCrossCorr", but saves time by reusing & updating previously stored
|
||||||
// "norm" value
|
// "norm" value
|
||||||
corr = calcCrossCorrAccumulate(refPos + channels * i, pMidBuffer, norm);
|
corr = calcCrossCorrAccumulate(refPos + channels * i, pMidBuffer, norm);
|
||||||
#endif
|
#endif
|
||||||
|
@ -328,7 +309,7 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
||||||
|
|
||||||
// Checks for the highest correlation value
|
// Checks for the highest correlation value
|
||||||
if (corr > bestCorr)
|
if (corr > bestCorr)
|
||||||
{
|
{
|
||||||
// For optimal performance, enter critical section only in case that best value found.
|
// For optimal performance, enter critical section only in case that best value found.
|
||||||
// in such case repeat 'if' condition as it's possible that parallel execution may have
|
// in such case repeat 'if' condition as it's possible that parallel execution may have
|
||||||
|
@ -353,14 +334,14 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Quick seek algorithm for improved runtime-performance: First roughly scans through the
|
// Quick seek algorithm for improved runtime-performance: First roughly scans through the
|
||||||
// correlation area, and then scan surroundings of two best preliminary correlation candidates
|
// correlation area, and then scan surroundings of two best preliminary correlation candidates
|
||||||
// with improved precision
|
// with improved precision
|
||||||
//
|
//
|
||||||
// Based on testing:
|
// Based on testing:
|
||||||
// - This algorithm gives on average 99% as good match as the full algorithm
|
// - This algorithm gives on average 99% as good match as the full algorithm
|
||||||
// - this quick seek algorithm finds the best match on ~90% of cases
|
// - this quick seek algorithm finds the best match on ~90% of cases
|
||||||
// - on those 10% of cases when this algorithm doesn't find best match,
|
// - on those 10% of cases when this algorithm doesn't find best match,
|
||||||
// it still finds on average ~90% match vs. the best possible match
|
// it still finds on average ~90% match vs. the best possible match
|
||||||
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||||
{
|
{
|
||||||
|
@ -379,7 +360,7 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||||
|
|
||||||
bestCorr =
|
bestCorr =
|
||||||
bestCorr2 = -FLT_MAX;
|
bestCorr2 = -FLT_MAX;
|
||||||
bestOffs =
|
bestOffs =
|
||||||
bestOffs2 = SCANWIND;
|
bestOffs2 = SCANWIND;
|
||||||
|
|
||||||
// Scans for the best correlation value by testing each possible position
|
// Scans for the best correlation value by testing each possible position
|
||||||
|
@ -387,7 +368,7 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||||
// increase possibility of ideal match.
|
// increase possibility of ideal match.
|
||||||
//
|
//
|
||||||
// Begin from "SCANSTEP" instead of SCANWIND to make the calculation
|
// Begin from "SCANSTEP" instead of SCANWIND to make the calculation
|
||||||
// catch the 'middlepoint' of seekLength vector as that's the a-priori
|
// catch the 'middlepoint' of seekLength vector as that's the a-priori
|
||||||
// expected best match position
|
// expected best match position
|
||||||
//
|
//
|
||||||
// Roughly:
|
// Roughly:
|
||||||
|
@ -475,7 +456,7 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// For integer algorithm: adapt normalization factor divider with music so that
|
/// For integer algorithm: adapt normalization factor divider with music so that
|
||||||
/// it'll not be pessimistically restrictive that can degrade quality on quieter sections
|
/// it'll not be pessimistically restrictive that can degrade quality on quieter sections
|
||||||
/// yet won't cause integer overflows either
|
/// yet won't cause integer overflows either
|
||||||
void TDStretch::adaptNormalizer()
|
void TDStretch::adaptNormalizer()
|
||||||
|
@ -483,7 +464,7 @@ void TDStretch::adaptNormalizer()
|
||||||
// Do not adapt normalizer over too silent sequences to avoid averaging filter depleting to
|
// Do not adapt normalizer over too silent sequences to avoid averaging filter depleting to
|
||||||
// too low values during pauses in music
|
// too low values during pauses in music
|
||||||
if ((maxnorm > 1000) || (maxnormf > 40000000))
|
if ((maxnorm > 1000) || (maxnormf > 40000000))
|
||||||
{
|
{
|
||||||
//norm averaging filter
|
//norm averaging filter
|
||||||
maxnormf = 0.9f * maxnormf + 0.1f * (float)maxnorm;
|
maxnormf = 0.9f * maxnormf + 0.1f * (float)maxnorm;
|
||||||
|
|
||||||
|
@ -504,7 +485,7 @@ void TDStretch::adaptNormalizer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// clear cross correlation routine state if necessary
|
/// clear cross correlation routine state if necessary
|
||||||
void TDStretch::clearCrossCorrState()
|
void TDStretch::clearCrossCorrState()
|
||||||
{
|
{
|
||||||
// default implementation is empty.
|
// default implementation is empty.
|
||||||
|
@ -534,7 +515,7 @@ void TDStretch::calcSeqParameters()
|
||||||
#define CHECK_LIMITS(x, mi, ma) (((x) < (mi)) ? (mi) : (((x) > (ma)) ? (ma) : (x)))
|
#define CHECK_LIMITS(x, mi, ma) (((x) < (mi)) ? (mi) : (((x) > (ma)) ? (ma) : (x)))
|
||||||
|
|
||||||
double seq, seek;
|
double seq, seek;
|
||||||
|
|
||||||
if (bAutoSeqSetting)
|
if (bAutoSeqSetting)
|
||||||
{
|
{
|
||||||
seq = AUTOSEQ_C + AUTOSEQ_K * tempo;
|
seq = AUTOSEQ_C + AUTOSEQ_K * tempo;
|
||||||
|
@ -551,7 +532,7 @@ void TDStretch::calcSeqParameters()
|
||||||
|
|
||||||
// Update seek window lengths
|
// Update seek window lengths
|
||||||
seekWindowLength = (sampleRate * sequenceMs) / 1000;
|
seekWindowLength = (sampleRate * sequenceMs) / 1000;
|
||||||
if (seekWindowLength < 2 * overlapLength)
|
if (seekWindowLength < 2 * overlapLength)
|
||||||
{
|
{
|
||||||
seekWindowLength = 2 * overlapLength;
|
seekWindowLength = 2 * overlapLength;
|
||||||
}
|
}
|
||||||
|
@ -560,7 +541,7 @@ void TDStretch::calcSeqParameters()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||||
// tempo, larger faster tempo.
|
// tempo, larger faster tempo.
|
||||||
void TDStretch::setTempo(double newTempo)
|
void TDStretch::setTempo(double newTempo)
|
||||||
{
|
{
|
||||||
|
@ -571,11 +552,11 @@ void TDStretch::setTempo(double newTempo)
|
||||||
// Calculate new sequence duration
|
// Calculate new sequence duration
|
||||||
calcSeqParameters();
|
calcSeqParameters();
|
||||||
|
|
||||||
// Calculate ideal skip length (according to tempo value)
|
// Calculate ideal skip length (according to tempo value)
|
||||||
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
||||||
intskip = (int)(nominalSkip + 0.5);
|
intskip = (int)(nominalSkip + 0.5);
|
||||||
|
|
||||||
// Calculate how many samples are needed in the 'inputBuffer' to
|
// Calculate how many samples are needed in the 'inputBuffer' to
|
||||||
// process another batch of samples
|
// process another batch of samples
|
||||||
//sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength / 2;
|
//sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength / 2;
|
||||||
sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength;
|
sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength;
|
||||||
|
@ -606,18 +587,18 @@ void TDStretch::processNominalTempo()
|
||||||
{
|
{
|
||||||
assert(tempo == 1.0f);
|
assert(tempo == 1.0f);
|
||||||
|
|
||||||
if (bMidBufferDirty)
|
if (bMidBufferDirty)
|
||||||
{
|
{
|
||||||
// If there are samples in pMidBuffer waiting for overlapping,
|
// If there are samples in pMidBuffer waiting for overlapping,
|
||||||
// do a single sliding overlapping with them in order to prevent a
|
// do a single sliding overlapping with them in order to prevent a
|
||||||
// clicking distortion in the output sound
|
// clicking distortion in the output sound
|
||||||
if (inputBuffer.numSamples() < overlapLength)
|
if (inputBuffer.numSamples() < overlapLength)
|
||||||
{
|
{
|
||||||
// wait until we've got overlapLength input samples
|
// wait until we've got overlapLength input samples
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Mix the samples in the beginning of 'inputBuffer' with the
|
// Mix the samples in the beginning of 'inputBuffer' with the
|
||||||
// samples in 'midBuffer' using sliding overlapping
|
// samples in 'midBuffer' using sliding overlapping
|
||||||
overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0);
|
overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0);
|
||||||
outputBuffer.putSamples(overlapLength);
|
outputBuffer.putSamples(overlapLength);
|
||||||
inputBuffer.receiveSamples(overlapLength);
|
inputBuffer.receiveSamples(overlapLength);
|
||||||
|
@ -642,7 +623,7 @@ void TDStretch::processSamples()
|
||||||
|
|
||||||
/* Removed this small optimization - can introduce a click to sound when tempo setting
|
/* Removed this small optimization - can introduce a click to sound when tempo setting
|
||||||
crosses the nominal value
|
crosses the nominal value
|
||||||
if (tempo == 1.0f)
|
if (tempo == 1.0f)
|
||||||
{
|
{
|
||||||
// tempo not changed from the original, so bypass the processing
|
// tempo not changed from the original, so bypass the processing
|
||||||
processNominalTempo();
|
processNominalTempo();
|
||||||
|
@ -652,15 +633,15 @@ void TDStretch::processSamples()
|
||||||
|
|
||||||
// Process samples as long as there are enough samples in 'inputBuffer'
|
// Process samples as long as there are enough samples in 'inputBuffer'
|
||||||
// to form a processing frame.
|
// to form a processing frame.
|
||||||
while ((int)inputBuffer.numSamples() >= sampleReq)
|
while ((int)inputBuffer.numSamples() >= sampleReq)
|
||||||
{
|
{
|
||||||
if (isBeginning == false)
|
if (isBeginning == false)
|
||||||
{
|
{
|
||||||
// apart from the very beginning of the track,
|
// apart from the very beginning of the track,
|
||||||
// scan for the best overlapping position & do overlap-add
|
// scan for the best overlapping position & do overlap-add
|
||||||
offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
|
offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
|
||||||
|
|
||||||
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
||||||
// samples in 'midBuffer' using sliding overlapping
|
// samples in 'midBuffer' using sliding overlapping
|
||||||
// ... first partially overlap with the end of the previous sequence
|
// ... first partially overlap with the end of the previous sequence
|
||||||
// (that's in 'midBuffer')
|
// (that's in 'midBuffer')
|
||||||
|
@ -705,11 +686,11 @@ void TDStretch::processSamples()
|
||||||
temp = (seekWindowLength - 2 * overlapLength);
|
temp = (seekWindowLength - 2 * overlapLength);
|
||||||
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * offset, (uint)temp);
|
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * offset, (uint)temp);
|
||||||
|
|
||||||
// Copies the end of the current sequence from 'inputBuffer' to
|
// Copies the end of the current sequence from 'inputBuffer' to
|
||||||
// 'midBuffer' for being mixed with the beginning of the next
|
// 'midBuffer' for being mixed with the beginning of the next
|
||||||
// processing sequence and so on
|
// processing sequence and so on
|
||||||
assert((offset + temp + overlapLength) <= (int)inputBuffer.numSamples());
|
assert((offset + temp + overlapLength) <= (int)inputBuffer.numSamples());
|
||||||
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp),
|
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp),
|
||||||
channels * sizeof(SAMPLETYPE) * overlapLength);
|
channels * sizeof(SAMPLETYPE) * overlapLength);
|
||||||
|
|
||||||
// Remove the processed samples from the input buffer. Update
|
// Remove the processed samples from the input buffer. Update
|
||||||
|
@ -757,9 +738,9 @@ void TDStretch::acceptNewOverlapLength(int newOverlapLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
// 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.
|
// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||||
void * TDStretch::operator new(size_t s)
|
void * TDStretch::operator new(size_t)
|
||||||
{
|
{
|
||||||
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
|
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
|
||||||
ST_THROW_RT_ERROR("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
|
ST_THROW_RT_ERROR("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
|
||||||
|
@ -772,6 +753,7 @@ TDStretch * TDStretch::newInstance()
|
||||||
uint uExtensions;
|
uint uExtensions;
|
||||||
|
|
||||||
uExtensions = detectCPUextensions();
|
uExtensions = detectCPUextensions();
|
||||||
|
(void)uExtensions;
|
||||||
|
|
||||||
// Check if MMX/SSE instruction set extensions supported by CPU
|
// Check if MMX/SSE instruction set extensions supported by CPU
|
||||||
|
|
||||||
|
@ -809,7 +791,7 @@ TDStretch * TDStretch::newInstance()
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||||
|
|
||||||
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
|
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
|
||||||
// version of the routine.
|
// version of the routine.
|
||||||
void TDStretch::overlapStereo(short *poutput, const short *input) const
|
void TDStretch::overlapStereo(short *poutput, const short *input) const
|
||||||
{
|
{
|
||||||
|
@ -862,8 +844,8 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
||||||
assert(aoverlapMs >= 0);
|
assert(aoverlapMs >= 0);
|
||||||
|
|
||||||
// calculate overlap length so that it's power of 2 - thus it's easy to do
|
// calculate overlap length so that it's power of 2 - thus it's easy to do
|
||||||
// integer division by right-shifting. Term "-1" at end is to account for
|
// integer division by right-shifting. Term "-1" at end is to account for
|
||||||
// the extra most significatnt bit left unused in result by signed multiplication
|
// the extra most significatnt bit left unused in result by signed multiplication
|
||||||
overlapDividerBitsPure = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
overlapDividerBitsPure = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
||||||
if (overlapDividerBitsPure > 9) overlapDividerBitsPure = 9;
|
if (overlapDividerBitsPure > 9) overlapDividerBitsPure = 9;
|
||||||
if (overlapDividerBitsPure < 3) overlapDividerBitsPure = 3;
|
if (overlapDividerBitsPure < 3) overlapDividerBitsPure = 3;
|
||||||
|
@ -873,8 +855,8 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
||||||
|
|
||||||
overlapDividerBitsNorm = overlapDividerBitsPure;
|
overlapDividerBitsNorm = overlapDividerBitsPure;
|
||||||
|
|
||||||
// calculate sloping divider so that crosscorrelation operation won't
|
// calculate sloping divider so that crosscorrelation operation won't
|
||||||
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
||||||
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
||||||
slopingDivider = (newOvl * newOvl - 1) / 3;
|
slopingDivider = (newOvl * newOvl - 1) / 3;
|
||||||
}
|
}
|
||||||
|
@ -898,9 +880,9 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
|
||||||
// Same routine for stereo and mono
|
// Same routine for stereo and mono
|
||||||
for (i = 0; i < ilength; i += 2)
|
for (i = 0; i < ilength; i += 2)
|
||||||
{
|
{
|
||||||
corr += (mixingPos[i] * compare[i] +
|
corr += (mixingPos[i] * compare[i] +
|
||||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm;
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm;
|
||||||
lnorm += (mixingPos[i] * mixingPos[i] +
|
lnorm += (mixingPos[i] * mixingPos[i] +
|
||||||
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBitsNorm;
|
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBitsNorm;
|
||||||
// do intermediate scalings to avoid integer overflow
|
// do intermediate scalings to avoid integer overflow
|
||||||
}
|
}
|
||||||
|
@ -914,7 +896,7 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
|
||||||
maxnorm = lnorm;
|
maxnorm = lnorm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
norm = (double)lnorm;
|
norm = (double)lnorm;
|
||||||
return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
|
return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
|
||||||
|
@ -940,9 +922,9 @@ double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *c
|
||||||
|
|
||||||
corr = 0;
|
corr = 0;
|
||||||
// Same routine for stereo and mono.
|
// Same routine for stereo and mono.
|
||||||
for (i = 0; i < ilength; i += 2)
|
for (i = 0; i < ilength; i += 2)
|
||||||
{
|
{
|
||||||
corr += (mixingPos[i] * compare[i] +
|
corr += (mixingPos[i] * compare[i] +
|
||||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm;
|
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -959,7 +941,7 @@ double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *c
|
||||||
maxnorm = (unsigned long)norm;
|
maxnorm = (unsigned long)norm;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
|
return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
|
||||||
}
|
}
|
||||||
|
@ -986,7 +968,7 @@ void TDStretch::overlapStereo(float *pOutput, const float *pInput) const
|
||||||
f1 = 0;
|
f1 = 0;
|
||||||
f2 = 1.0f;
|
f2 = 1.0f;
|
||||||
|
|
||||||
for (i = 0; i < 2 * (int)overlapLength ; i += 2)
|
for (i = 0; i < 2 * (int)overlapLength ; i += 2)
|
||||||
{
|
{
|
||||||
pOutput[i + 0] = pInput[i + 0] * f1 + pMidBuffer[i + 0] * f2;
|
pOutput[i + 0] = pInput[i + 0] * f1 + pMidBuffer[i + 0] * f2;
|
||||||
pOutput[i + 1] = pInput[i + 1] * f1 + pMidBuffer[i + 1] * f2;
|
pOutput[i + 1] = pInput[i + 1] * f1 + pMidBuffer[i + 1] * f2;
|
||||||
|
@ -997,7 +979,7 @@ void TDStretch::overlapStereo(float *pOutput, const float *pInput) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Overlaps samples in 'midBuffer' with the samples in 'input'.
|
// Overlaps samples in 'midBuffer' with the samples in 'input'.
|
||||||
void TDStretch::overlapMulti(float *pOutput, const float *pInput) const
|
void TDStretch::overlapMulti(float *pOutput, const float *pInput) const
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||||
/// with several performance-increasing tweaks.
|
/// with several performance-increasing tweaks.
|
||||||
///
|
///
|
||||||
/// Note : MMX/SSE optimized functions reside in separate, platform-specific files
|
/// Note : MMX/SSE optimized functions reside in separate, platform-specific files
|
||||||
/// 'mmx_optimized.cpp' and 'sse_optimized.cpp'
|
/// 'mmx_optimized.cpp' and 'sse_optimized.cpp'
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -46,14 +46,14 @@ 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
|
/// Notice that the default parameters are tuned for contemporary popular music
|
||||||
/// processing. For speech processing applications these parameters suit better:
|
/// processing. For speech processing applications these parameters suit better:
|
||||||
/// #define DEFAULT_SEQUENCE_MS 40
|
/// #define DEFAULT_SEQUENCE_MS 40
|
||||||
/// #define DEFAULT_SEEKWINDOW_MS 15
|
/// #define DEFAULT_SEEKWINDOW_MS 15
|
||||||
/// #define DEFAULT_OVERLAP_MS 8
|
/// #define DEFAULT_OVERLAP_MS 8
|
||||||
///
|
///
|
||||||
|
|
||||||
/// Default length of a single processing sequence, in milliseconds. This determines to how
|
/// 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.
|
/// long sequences the original sound is chopped in the time-stretch algorithm.
|
||||||
///
|
///
|
||||||
/// The larger this value is, the lesser sequences are used in processing. In principle
|
/// The larger this value is, the lesser sequences are used in processing. In principle
|
||||||
|
@ -68,15 +68,15 @@ namespace soundtouch
|
||||||
/// according to tempo setting (recommended)
|
/// according to tempo setting (recommended)
|
||||||
#define USE_AUTO_SEQUENCE_LEN 0
|
#define USE_AUTO_SEQUENCE_LEN 0
|
||||||
|
|
||||||
/// Seeking window default length in milliseconds for algorithm that finds the best possible
|
/// 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
|
/// overlapping location. This determines from how wide window the algorithm may look for an
|
||||||
/// optimal joining location when mixing the sound sequences back together.
|
/// 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
|
/// 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
|
/// 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.
|
/// 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
|
/// If there's a disturbing artifact that sounds as if a constant frequency was drifting
|
||||||
/// around, try reducing this setting.
|
/// around, try reducing this setting.
|
||||||
///
|
///
|
||||||
/// Increasing this value increases computational burden & vice versa.
|
/// Increasing this value increases computational burden & vice versa.
|
||||||
|
@ -87,11 +87,11 @@ namespace soundtouch
|
||||||
/// according to tempo setting (recommended)
|
/// according to tempo setting (recommended)
|
||||||
#define USE_AUTO_SEEKWINDOW_LEN 0
|
#define USE_AUTO_SEEKWINDOW_LEN 0
|
||||||
|
|
||||||
/// Overlap length in milliseconds. When the chopped sound sequences are mixed back together,
|
/// 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
|
/// to form a continuous sound stream, this parameter defines over how long period the two
|
||||||
/// consecutive sequences are let to overlap each other.
|
/// consecutive sequences are let to overlap each other.
|
||||||
///
|
///
|
||||||
/// This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting
|
/// 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.
|
/// by a large amount, you might wish to try a smaller value on this.
|
||||||
///
|
///
|
||||||
/// Increasing this value increases computational burden & vice versa.
|
/// Increasing this value increases computational burden & vice versa.
|
||||||
|
@ -162,27 +162,27 @@ protected:
|
||||||
/// The maximum amount of samples that can be returned at a time is set by
|
/// The maximum amount of samples that can be returned at a time is set by
|
||||||
/// the 'set_returnBuffer_size' function.
|
/// the 'set_returnBuffer_size' function.
|
||||||
void processSamples();
|
void processSamples();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TDStretch();
|
TDStretch();
|
||||||
virtual ~TDStretch() override;
|
virtual ~TDStretch() override;
|
||||||
|
|
||||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
/// 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.
|
/// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||||
static 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.
|
/// 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
|
/// This function automatically chooses a correct feature set depending on if the CPU
|
||||||
/// supports MMX/SSE/etc extensions.
|
/// supports MMX/SSE/etc extensions.
|
||||||
static TDStretch *newInstance();
|
static TDStretch *newInstance();
|
||||||
|
|
||||||
/// Returns the output buffer object
|
/// Returns the output buffer object
|
||||||
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
||||||
|
|
||||||
/// Returns the input buffer object
|
/// Returns the input buffer object
|
||||||
FIFOSamplePipe *getInput() { return &inputBuffer; };
|
FIFOSamplePipe *getInput() { return &inputBuffer; };
|
||||||
|
|
||||||
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||||
/// tempo, larger faster tempo.
|
/// tempo, larger faster tempo.
|
||||||
void setTempo(double newTempo);
|
void setTempo(double newTempo);
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ public:
|
||||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||||
void setChannels(int numChannels);
|
void setChannels(int numChannels);
|
||||||
|
|
||||||
/// Enables/disables the quick position seeking algorithm. Zero to disable,
|
/// Enables/disables the quick position seeking algorithm. Zero to disable,
|
||||||
/// nonzero to enable
|
/// nonzero to enable
|
||||||
void enableQuickSeek(bool enable);
|
void enableQuickSeek(bool enable);
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ public:
|
||||||
//
|
//
|
||||||
/// 'sampleRate' = sample rate of the sound
|
/// 'sampleRate' = sample rate of the sound
|
||||||
/// 'sequenceMS' = one processing sequence length in milliseconds
|
/// 'sequenceMS' = one processing sequence length in milliseconds
|
||||||
/// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
/// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||||
/// position
|
/// position
|
||||||
/// 'overlapMS' = overlapping length
|
/// 'overlapMS' = overlapping length
|
||||||
void setParameters(int sampleRate, ///< Samplerate of sound being processed (Hz)
|
void setParameters(int sampleRate, ///< Samplerate of sound being processed (Hz)
|
||||||
|
@ -217,7 +217,7 @@ public:
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Get routine control parameters, see setParameters() function.
|
/// Get routine control parameters, see setParameters() function.
|
||||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
/// Any of the parameters to this function can be nullptr, in such case corresponding parameter
|
||||||
/// value isn't returned.
|
/// value isn't returned.
|
||||||
void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const;
|
void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const;
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
///
|
///
|
||||||
/// A header file for detecting the Intel MMX instructions set extension.
|
/// A header file for detecting the Intel MMX instructions set extension.
|
||||||
///
|
///
|
||||||
/// Please see 'mmx_win.cpp', 'mmx_cpp.cpp' and 'mmx_non_x86.cpp' for the
|
/// Please see 'mmx_win.cpp', 'mmx_cpp.cpp' and 'mmx_non_x86.cpp' for the
|
||||||
/// routine implementations for x86 Windows, x86 gnu version and non-x86
|
/// routine implementations for x86 Windows, x86 gnu version and non-x86
|
||||||
/// platforms, respectively.
|
/// platforms, respectively.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
///
|
///
|
||||||
/// Generic version of the x86 CPU extension detection routine.
|
/// Generic version of the x86 CPU extension detection routine.
|
||||||
///
|
///
|
||||||
/// This file is for GNU & other non-Windows compilers, see 'cpu_detect_x86_win.cpp'
|
/// This file is for GNU & other non-Windows compilers, see 'cpu_detect_x86_win.cpp'
|
||||||
/// for the Microsoft compiler version.
|
/// for the Microsoft compiler version.
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -86,9 +86,9 @@ uint detectCPUextensions(void)
|
||||||
&& defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
|
&& defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
|
||||||
|
|
||||||
if (_dwDisabledISA == 0xffffffff) return 0;
|
if (_dwDisabledISA == 0xffffffff) return 0;
|
||||||
|
|
||||||
uint res = 0;
|
uint res = 0;
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
// GCC version of cpuid. Requires GCC 4.3.0 or later for __cpuid intrinsic support.
|
// GCC version of cpuid. Requires GCC 4.3.0 or later for __cpuid intrinsic support.
|
||||||
uint eax, ebx, ecx, edx; // unsigned int is the standard type. uint is defined by the compiler and not guaranteed to be portable.
|
uint eax, ebx, ecx, edx; // unsigned int is the standard type. uint is defined by the compiler and not guaranteed to be portable.
|
||||||
|
@ -101,7 +101,7 @@ uint detectCPUextensions(void)
|
||||||
if (edx & bit_SSE2) res = res | SUPPORT_SSE2;
|
if (edx & bit_SSE2) res = res | SUPPORT_SSE2;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// Window / VS version of cpuid. Notice that Visual Studio 2005 or later required
|
// Window / VS version of cpuid. Notice that Visual Studio 2005 or later required
|
||||||
// for __cpuid intrinsic support.
|
// for __cpuid intrinsic support.
|
||||||
int reg[4] = {-1};
|
int reg[4] = {-1};
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// MMX optimized routines. All MMX optimized functions have been gathered into
|
/// MMX optimized routines. All MMX optimized functions have been gathered into
|
||||||
/// this single source code file, regardless to their class or original source
|
/// this single source code file, regardless to their class or original source
|
||||||
/// code file, in order to ease porting the library to other compiler and
|
/// code file, in order to ease porting the library to other compiler and
|
||||||
/// processor platforms.
|
/// processor platforms.
|
||||||
///
|
///
|
||||||
/// The MMX-optimizations are programmed using MMX compiler intrinsics that
|
/// The MMX-optimizations are programmed using MMX compiler intrinsics that
|
||||||
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
||||||
/// should compile with both toolsets.
|
/// should compile with both toolsets.
|
||||||
///
|
///
|
||||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
/// 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
|
/// 6.0 processor pack" update to support compiler intrinsic syntax. The update
|
||||||
/// is available for download at Microsoft Developers Network, see here:
|
/// is available for download at Microsoft Developers Network, see here:
|
||||||
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
|
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
|
||||||
|
@ -68,14 +68,14 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
||||||
__m64 accu, normaccu;
|
__m64 accu, normaccu;
|
||||||
long corr, norm;
|
long corr, norm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
pVec1 = (__m64*)pV1;
|
pVec1 = (__m64*)pV1;
|
||||||
pVec2 = (__m64*)pV2;
|
pVec2 = (__m64*)pV2;
|
||||||
|
|
||||||
shifter = _m_from_int(overlapDividerBitsNorm);
|
shifter = _m_from_int(overlapDividerBitsNorm);
|
||||||
normaccu = accu = _mm_setzero_si64();
|
normaccu = accu = _mm_setzero_si64();
|
||||||
|
|
||||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||||
// during each round for improved CPU-level parallellization.
|
// during each round for improved CPU-level parallellization.
|
||||||
for (i = 0; i < channels * overlapLength / 16; i ++)
|
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||||
{
|
{
|
||||||
|
@ -126,7 +126,7 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
dnorm = (double)norm;
|
dnorm = (double)norm;
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2,
|
||||||
__m64 accu;
|
__m64 accu;
|
||||||
long corr, lnorm;
|
long corr, lnorm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
// cancel first normalizer tap from previous round
|
// cancel first normalizer tap from previous round
|
||||||
lnorm = 0;
|
lnorm = 0;
|
||||||
for (i = 1; i <= channels; i ++)
|
for (i = 1; i <= channels; i ++)
|
||||||
|
@ -158,7 +158,7 @@ double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2,
|
||||||
shifter = _m_from_int(overlapDividerBitsNorm);
|
shifter = _m_from_int(overlapDividerBitsNorm);
|
||||||
accu = _mm_setzero_si64();
|
accu = _mm_setzero_si64();
|
||||||
|
|
||||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||||
// during each round for improved CPU-level parallellization.
|
// during each round for improved CPU-level parallellization.
|
||||||
for (i = 0; i < channels * overlapLength / 16; i ++)
|
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||||
{
|
{
|
||||||
|
@ -203,7 +203,7 @@ double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2,
|
||||||
maxnorm = lnorm;
|
maxnorm = lnorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||||
// done using floating point operation
|
// done using floating point operation
|
||||||
return (double)corr / sqrt((dnorm < 1e-9) ? 1.0 : dnorm);
|
return (double)corr / sqrt((dnorm < 1e-9) ? 1.0 : dnorm);
|
||||||
}
|
}
|
||||||
|
@ -232,7 +232,7 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||||
// mix1 = mixer values for 1st stereo sample
|
// mix1 = mixer values for 1st stereo sample
|
||||||
// mix1 = mixer values for 2nd stereo sample
|
// mix1 = mixer values for 2nd stereo sample
|
||||||
// adder = adder for updating mixer values after each round
|
// adder = adder for updating mixer values after each round
|
||||||
|
|
||||||
mix1 = _mm_set_pi16(0, overlapLength, 0, overlapLength);
|
mix1 = _mm_set_pi16(0, overlapLength, 0, overlapLength);
|
||||||
adder = _mm_set_pi16(1, -1, 1, -1);
|
adder = _mm_set_pi16(1, -1, 1, -1);
|
||||||
mix2 = _mm_add_pi16(mix1, adder);
|
mix2 = _mm_add_pi16(mix1, adder);
|
||||||
|
@ -245,7 +245,7 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||||
for (i = 0; i < overlapLength / 4; i ++)
|
for (i = 0; i < overlapLength / 4; i ++)
|
||||||
{
|
{
|
||||||
__m64 temp1, temp2;
|
__m64 temp1, temp2;
|
||||||
|
|
||||||
// load & shuffle data so that input & mixbuffer data samples are paired
|
// load & shuffle data so that input & mixbuffer data samples are paired
|
||||||
temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]); // = i0l m0l i0r m0r
|
temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]); // = i0l m0l i0r m0r
|
||||||
temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]); // = i1l m1l i1r m1r
|
temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]); // = i1l m1l i1r m1r
|
||||||
|
@ -294,8 +294,8 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||||
|
|
||||||
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
||||||
{
|
{
|
||||||
filterCoeffsAlign = NULL;
|
filterCoeffsAlign = nullptr;
|
||||||
filterCoeffsUnalign = NULL;
|
filterCoeffsUnalign = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -316,8 +316,8 @@ void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uRe
|
||||||
filterCoeffsUnalign = new short[2 * newLength + 8];
|
filterCoeffsUnalign = new short[2 * newLength + 8];
|
||||||
filterCoeffsAlign = (short *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign);
|
filterCoeffsAlign = (short *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign);
|
||||||
|
|
||||||
// rearrange the filter coefficients for mmx routines
|
// rearrange the filter coefficients for mmx routines
|
||||||
for (i = 0;i < length; i += 4)
|
for (i = 0;i < length; i += 4)
|
||||||
{
|
{
|
||||||
filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
|
filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
|
||||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
|
filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
///
|
///
|
||||||
/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE
|
/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE
|
||||||
/// optimized functions have been gathered into this single source
|
/// optimized functions have been gathered into this single source
|
||||||
/// code file, regardless to their class or original source code file, in order
|
/// code file, regardless to their class or original source code file, in order
|
||||||
/// to ease porting the library to other compiler and processor platforms.
|
/// to ease porting the library to other compiler and processor platforms.
|
||||||
///
|
///
|
||||||
/// The SSE-optimizations are programmed using SSE compiler intrinsics that
|
/// The SSE-optimizations are programmed using SSE compiler intrinsics that
|
||||||
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
||||||
/// should compile with both toolsets.
|
/// should compile with both toolsets.
|
||||||
///
|
///
|
||||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
/// 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
|
/// 6.0 processor pack" update to support SSE instruction set. The update is
|
||||||
/// available for download at Microsoft Developers Network, see here:
|
/// available for download at Microsoft Developers Network, see here:
|
||||||
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.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
|
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
|
||||||
/// perform a search with keywords "processor pack".
|
/// perform a search with keywords "processor pack".
|
||||||
///
|
///
|
||||||
/// Author : Copyright (c) Olli Parviainen
|
/// Author : Copyright (c) Olli Parviainen
|
||||||
|
@ -51,7 +51,7 @@ using namespace soundtouch;
|
||||||
|
|
||||||
#ifdef SOUNDTOUCH_ALLOW_SSE
|
#ifdef SOUNDTOUCH_ALLOW_SSE
|
||||||
|
|
||||||
// SSE routines available only with float sample type
|
// SSE routines available only with float sample type
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
@ -71,8 +71,8 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||||
const __m128 *pVec2;
|
const __m128 *pVec2;
|
||||||
__m128 vSum, vNorm;
|
__m128 vSum, vNorm;
|
||||||
|
|
||||||
// Note. It means a major slow-down if the routine needs to tolerate
|
// 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
|
// unaligned __m128 memory accesses. It's way faster if we can skip
|
||||||
// unaligned slots and use _mm_load_ps instruction instead of _mm_loadu_ps.
|
// unaligned slots and use _mm_load_ps instruction instead of _mm_loadu_ps.
|
||||||
// This can mean up to ~ 10-fold difference (incl. part of which is
|
// This can mean up to ~ 10-fold difference (incl. part of which is
|
||||||
// due to skipping every second round for stereo sound though).
|
// due to skipping every second round for stereo sound though).
|
||||||
|
@ -81,7 +81,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||||
// for choosing if this little cheating is allowed.
|
// for choosing if this little cheating is allowed.
|
||||||
|
|
||||||
#ifdef ST_SIMD_AVOID_UNALIGNED
|
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||||
// Little cheating allowed, return valid correlation only for
|
// Little cheating allowed, return valid correlation only for
|
||||||
// aligned locations, meaning every second round for stereo sound.
|
// aligned locations, meaning every second round for stereo sound.
|
||||||
|
|
||||||
#define _MM_LOAD _mm_load_ps
|
#define _MM_LOAD _mm_load_ps
|
||||||
|
@ -92,7 +92,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||||
// No cheating allowed, use unaligned load & take the resulting
|
// No cheating allowed, use unaligned load & take the resulting
|
||||||
// performance hit.
|
// performance hit.
|
||||||
#define _MM_LOAD _mm_loadu_ps
|
#define _MM_LOAD _mm_loadu_ps
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// ensure overlapLength is divisible by 8
|
// ensure overlapLength is divisible by 8
|
||||||
assert((overlapLength % 8) == 0);
|
assert((overlapLength % 8) == 0);
|
||||||
|
@ -105,7 +105,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||||
|
|
||||||
// Unroll the loop by factor of 4 * 4 operations. Use same routine for
|
// Unroll the loop by factor of 4 * 4 operations. Use same routine for
|
||||||
// stereo & mono, for mono it just means twice the amount of unrolling.
|
// stereo & mono, for mono it just means twice the amount of unrolling.
|
||||||
for (i = 0; i < channels * overlapLength / 16; i ++)
|
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||||
{
|
{
|
||||||
__m128 vTemp;
|
__m128 vTemp;
|
||||||
// vSum += pV1[0..3] * pV2[0..3]
|
// vSum += pV1[0..3] * pV2[0..3]
|
||||||
|
@ -146,7 +146,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||||
|
|
||||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||||
corr = norm = 0.0;
|
corr = norm = 0.0;
|
||||||
for (i = 0; i < channels * overlapLength / 16; i ++)
|
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||||
{
|
{
|
||||||
corr += pV1[0] * pV2[0] +
|
corr += pV1[0] * pV2[0] +
|
||||||
pV1[1] * pV2[1] +
|
pV1[1] * pV2[1] +
|
||||||
|
@ -178,8 +178,8 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||||
|
|
||||||
double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, double &norm)
|
double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, double &norm)
|
||||||
{
|
{
|
||||||
// call usual calcCrossCorr function because SSE does not show big benefit of
|
// call usual calcCrossCorr function because SSE does not show big benefit of
|
||||||
// accumulating "norm" value, and also the "norm" rolling algorithm would get
|
// accumulating "norm" value, and also the "norm" rolling algorithm would get
|
||||||
// complicated due to SSE-specific alignment-vs-nonexact correlation rules.
|
// complicated due to SSE-specific alignment-vs-nonexact correlation rules.
|
||||||
return calcCrossCorr(pV1, pV2, norm);
|
return calcCrossCorr(pV1, pV2, norm);
|
||||||
}
|
}
|
||||||
|
@ -195,16 +195,16 @@ double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2,
|
||||||
|
|
||||||
FIRFilterSSE::FIRFilterSSE() : FIRFilter()
|
FIRFilterSSE::FIRFilterSSE() : FIRFilter()
|
||||||
{
|
{
|
||||||
filterCoeffsAlign = NULL;
|
filterCoeffsAlign = nullptr;
|
||||||
filterCoeffsUnalign = NULL;
|
filterCoeffsUnalign = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
FIRFilterSSE::~FIRFilterSSE()
|
FIRFilterSSE::~FIRFilterSSE()
|
||||||
{
|
{
|
||||||
delete[] filterCoeffsUnalign;
|
delete[] filterCoeffsUnalign;
|
||||||
filterCoeffsAlign = NULL;
|
filterCoeffsAlign = nullptr;
|
||||||
filterCoeffsUnalign = NULL;
|
filterCoeffsUnalign = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uRe
|
||||||
|
|
||||||
fDivider = (float)resultDivider;
|
fDivider = (float)resultDivider;
|
||||||
|
|
||||||
// rearrange the filter coefficients for mmx routines
|
// rearrange the filter coefficients for mmx routines
|
||||||
for (i = 0; i < newLength; i ++)
|
for (i = 0; i < newLength; i ++)
|
||||||
{
|
{
|
||||||
filterCoeffsAlign[2 * i + 0] =
|
filterCoeffsAlign[2 * i + 0] =
|
||||||
|
@ -245,10 +245,10 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
||||||
|
|
||||||
if (count < 2) return 0;
|
if (count < 2) return 0;
|
||||||
|
|
||||||
assert(source != NULL);
|
assert(source != nullptr);
|
||||||
assert(dest != NULL);
|
assert(dest != nullptr);
|
||||||
assert((length % 8) == 0);
|
assert((length % 8) == 0);
|
||||||
assert(filterCoeffsAlign != NULL);
|
assert(filterCoeffsAlign != nullptr);
|
||||||
assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
|
assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
|
||||||
|
|
||||||
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
||||||
|
@ -263,13 +263,13 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
||||||
|
|
||||||
pSrc = (const float*)source + j * 2; // source audio data
|
pSrc = (const float*)source + j * 2; // source audio data
|
||||||
pDest = dest + j * 2; // destination audio data
|
pDest = dest + j * 2; // destination audio data
|
||||||
pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
|
pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
|
||||||
// are aligned to 16-byte boundary
|
// are aligned to 16-byte boundary
|
||||||
sum1 = sum2 = _mm_setzero_ps();
|
sum1 = sum2 = _mm_setzero_ps();
|
||||||
|
|
||||||
for (i = 0; i < length / 8; i ++)
|
for (i = 0; i < length / 8; i ++)
|
||||||
{
|
{
|
||||||
// Unroll loop for efficiency & calculate filter for 2*2 stereo samples
|
// Unroll loop for efficiency & calculate filter for 2*2 stereo samples
|
||||||
// at each pass
|
// at each pass
|
||||||
|
|
||||||
// sum1 is accu for 2*2 filtered stereo sound data at the primary sound data offset
|
// sum1 is accu for 2*2 filtered stereo sound data at the primary sound data offset
|
||||||
|
@ -302,14 +302,14 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ideas for further improvement:
|
// Ideas for further improvement:
|
||||||
// 1. If it could be guaranteed that 'source' were always aligned to 16-byte
|
// 1. If it could be guaranteed that 'source' were always aligned to 16-byte
|
||||||
// boundary, a faster aligned '_mm_load_ps' instruction could be used.
|
// boundary, a faster aligned '_mm_load_ps' instruction could be used.
|
||||||
// 2. If it could be guaranteed that 'dest' were always aligned to 16-byte
|
// 2. If it could be guaranteed that 'dest' were always aligned to 16-byte
|
||||||
// boundary, a faster '_mm_store_ps' instruction could be used.
|
// boundary, a faster '_mm_store_ps' instruction could be used.
|
||||||
|
|
||||||
return (uint)count;
|
return (uint)count;
|
||||||
|
|
||||||
/* original routine in C-language. please notice the C-version has differently
|
/* original routine in C-language. please notice the C-version has differently
|
||||||
organized coefficients though.
|
organized coefficients though.
|
||||||
double suml1, suml2;
|
double suml1, suml2;
|
||||||
double sumr1, sumr2;
|
double sumr1, sumr2;
|
||||||
|
@ -324,26 +324,26 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
||||||
suml2 = sumr2 = 0.0;
|
suml2 = sumr2 = 0.0;
|
||||||
ptr = src;
|
ptr = src;
|
||||||
pFil = filterCoeffs;
|
pFil = filterCoeffs;
|
||||||
for (i = 0; i < lengthLocal; i ++)
|
for (i = 0; i < lengthLocal; i ++)
|
||||||
{
|
{
|
||||||
// unroll loop for efficiency.
|
// unroll loop for efficiency.
|
||||||
|
|
||||||
suml1 += ptr[0] * pFil[0] +
|
suml1 += ptr[0] * pFil[0] +
|
||||||
ptr[2] * pFil[2] +
|
ptr[2] * pFil[2] +
|
||||||
ptr[4] * pFil[4] +
|
ptr[4] * pFil[4] +
|
||||||
ptr[6] * pFil[6];
|
ptr[6] * pFil[6];
|
||||||
|
|
||||||
sumr1 += ptr[1] * pFil[1] +
|
sumr1 += ptr[1] * pFil[1] +
|
||||||
ptr[3] * pFil[3] +
|
ptr[3] * pFil[3] +
|
||||||
ptr[5] * pFil[5] +
|
ptr[5] * pFil[5] +
|
||||||
ptr[7] * pFil[7];
|
ptr[7] * pFil[7];
|
||||||
|
|
||||||
suml2 += ptr[8] * pFil[0] +
|
suml2 += ptr[8] * pFil[0] +
|
||||||
ptr[10] * pFil[2] +
|
ptr[10] * pFil[2] +
|
||||||
ptr[12] * pFil[4] +
|
ptr[12] * pFil[4] +
|
||||||
ptr[14] * pFil[6];
|
ptr[14] * pFil[6];
|
||||||
|
|
||||||
sumr2 += ptr[9] * pFil[1] +
|
sumr2 += ptr[9] * pFil[1] +
|
||||||
ptr[11] * pFil[3] +
|
ptr[11] * pFil[3] +
|
||||||
ptr[13] * pFil[5] +
|
ptr[13] * pFil[5] +
|
||||||
ptr[15] * pFil[7];
|
ptr[15] * pFil[7];
|
||||||
|
|
Loading…
Reference in New Issue