From c31fca9c9fa12abfd3dbd20f4b2954b9bd6a71c9 Mon Sep 17 00:00:00 2001 From: oparviai Date: Sun, 5 Mar 2017 16:36:35 +0000 Subject: [PATCH] Added int16/short sample version of putSamples()/receiveSamples() functions to SoundTouch.dll API --- include/SoundTouch.h | 5 + source/SoundTouchDLL/DllTest/DllTest.cpp | 114 +++++++++++++ source/SoundTouchDLL/DllTest/DllTest.vcxproj | 163 +++++++++++++++++++ source/SoundTouchDLL/SoundTouchDLL.cpp | 139 +++++++++++++--- source/SoundTouchDLL/SoundTouchDLL.h | 48 ++++-- 5 files changed, 431 insertions(+), 38 deletions(-) create mode 100644 source/SoundTouchDLL/DllTest/DllTest.cpp create mode 100644 source/SoundTouchDLL/DllTest/DllTest.vcxproj diff --git a/include/SoundTouch.h b/include/SoundTouch.h index edbee84..50decc6 100644 --- a/include/SoundTouch.h +++ b/include/SoundTouch.h @@ -335,6 +335,11 @@ public: /// Returns number of samples currently unprocessed. virtual uint numUnprocessedSamples() const; + /// Return number of channels + uint numChannels() const + { + return channels; + } /// Other handy functions that are implemented in the ancestor classes (see /// classes 'FIFOProcessor' and 'FIFOSamplePipe') diff --git a/source/SoundTouchDLL/DllTest/DllTest.cpp b/source/SoundTouchDLL/DllTest/DllTest.cpp new file mode 100644 index 0000000..666405d --- /dev/null +++ b/source/SoundTouchDLL/DllTest/DllTest.cpp @@ -0,0 +1,114 @@ +//////////////////////////////////////////////////////////////////////////////// +/// +/// DllTest.cpp : This is small app main routine used for testing sound processing +/// with SoundTouch.dll API +/// +/// Author : Copyright (c) Olli Parviainen +/// Author e-mail : oparviai 'at' iki.fi +/// SoundTouch WWW: http://www.surina.net/soundtouch +/// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include "../SoundTouchDLL.h" +#include "../../SoundStretch/WavFile.h" + +using namespace std; + +// DllTest main +int main(int argc, char *argv[]) +{ + // Check program arguments + if (argc < 4) + { + cout << "Too few arguments. Usage: DllTest [infile.wav] [outfile.wav] [sampletype]" << endl; + return -1; + } + + const char *inFileName = argv[1]; + const char *outFileName = argv[2]; + string str_sampleType = argv[3]; + + bool floatSample; + if (str_sampleType.compare("float") == 0) + { + floatSample = true; + } + else if (str_sampleType.compare("short") == 0) + { + floatSample = false; + } + else + { + cerr << "Missing or invalid sampletype '" << str_sampleType << "'. Expected either short or float" << endl; + return -1; + } + + try + { + // Open input & output WAV files + WavInFile inFile(inFileName); + int numChannels = inFile.getNumChannels(); + int sampleRate = inFile.getSampleRate(); + WavOutFile outFile(outFileName, sampleRate, inFile.getNumBits(), numChannels); + + // Create SoundTouch DLL instance + HANDLE st = soundtouch_createInstance(); + soundtouch_setChannels(st, numChannels); + soundtouch_setSampleRate(st, sampleRate); + soundtouch_setPitchSemiTones(st, 2); + + cout << "processing with soundtouch.dll routines"; + + if (floatSample) + { + // Process file with SoundTouch.DLL float sample (default) API + float fbuffer[2048]; + int nmax = 2048 / numChannels; + + cout << " using float api ..." << endl; + while (inFile.eof() == false) + { + int n = inFile.read(fbuffer, nmax * numChannels) / numChannels; + soundtouch_putSamples(st, fbuffer, n); + do + { + n = soundtouch_receiveSamples(st, fbuffer, nmax); + outFile.write(fbuffer, n * numChannels); + } while (n > 0); + } + } + else + { + // Process file with SoundTouch.DLL int16 (short) sample API. + // Notice that SoundTouch.dll does internally processing using floating + // point routines so the int16 API is not any faster, but provided for + // convenience. + short i16buffer[2048]; + int nmax = 2048 / numChannels; + + cout << " using i16 api ..." << endl; + while (inFile.eof() == false) + { + int n = inFile.read(i16buffer, nmax * numChannels) / numChannels; + soundtouch_putSamples_i16(st, i16buffer, n); + do + { + n = soundtouch_receiveSamples_i16(st, i16buffer, nmax); + outFile.write(i16buffer, n * numChannels); + } while (n > 0); + } + } + + soundtouch_destroyInstance(st); + cout << "done." << endl; + } + catch (const runtime_error &e) + { + cerr << e.what() << endl; + } + + return 0; +} diff --git a/source/SoundTouchDLL/DllTest/DllTest.vcxproj b/source/SoundTouchDLL/DllTest/DllTest.vcxproj new file mode 100644 index 0000000..82147f3 --- /dev/null +++ b/source/SoundTouchDLL/DllTest/DllTest.vcxproj @@ -0,0 +1,163 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {E3C0726F-28F4-4F0B-8183-B87CA60C063C} + Win32Proj + DllTest + 8.1 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + ..\..\..\include + + + Console + true + ..\..\..\lib\SoundTouchDllD.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)D$(TargetExt) + + + + + + + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ..\..\..\include + + + Console + true + true + true + ..\..\..\lib\SoundTouchDll.lib + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/SoundTouchDLL/SoundTouchDLL.cpp b/source/SoundTouchDLL/SoundTouchDLL.cpp index 447c4a3..a3e016a 100644 --- a/source/SoundTouchDLL/SoundTouchDLL.cpp +++ b/source/SoundTouchDLL/SoundTouchDLL.cpp @@ -41,19 +41,24 @@ using namespace soundtouch; +#ifdef SOUNDTOUCH_INTEGER_SAMPLES + #error "error - compile the dll version with float samples" +#endif // SOUNDTOUCH_INTEGER_SAMPLES + + BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved - ) + ) { - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } return TRUE; } @@ -229,10 +234,10 @@ SOUNDTOUCHDLL_API void __cdecl soundtouch_flush(HANDLE h) /// the input of the object. Notice that sample rate _has_to_ be set before /// calling this function, otherwise throws a runtime_error exception. SOUNDTOUCHDLL_API void __cdecl soundtouch_putSamples(HANDLE h, - const SAMPLETYPE *samples, ///< Pointer to sample buffer. - uint numSamples ///< Number of samples in buffer. Notice - ///< that in case of stereo-sound a single sample - ///< contains data for both channels. + const SAMPLETYPE *samples, ///< Pointer to sample buffer. + unsigned int numSamples ///< Number of samples in buffer. Notice + ///< that in case of stereo-sound a single sample + ///< contains data for both channels. ) { STHANDLE *sth = (STHANDLE*)h; @@ -241,6 +246,42 @@ SOUNDTOUCHDLL_API void __cdecl soundtouch_putSamples(HANDLE h, sth->pst->putSamples(samples, numSamples); } +/// int16 version of soundtouch_putSamples(): This accept int16 (short) sample data +/// and internally converts it to float format before processing +SOUNDTOUCHDLL_API void __cdecl soundtouch_putSamples_i16(HANDLE h, + const short *samples, ///< Pointer to sample buffer. + unsigned int numSamples ///< Number of sample frames in buffer. Notice + ///< that in case of multi-channel sound a single sample + ///< contains data for all channels. + ) +{ + STHANDLE *sth = (STHANDLE*)h; + if (sth->dwMagic != STMAGIC) return; + + uint numChannels = sth->pst->numChannels(); + + // iterate until all samples converted & put to SoundTouch object + while (numSamples > 0) + { + float convert[8192]; // allocate temporary conversion buffer from stack + + // how many multichannel samples fit into 'convert' buffer: + uint convSamples = 8192 / numChannels; + + // convert max 'nround' values at a time to guarantee that these fit in the 'convert' buffer + uint n = (numSamples > convSamples) ? convSamples : numSamples; + for (uint i = 0; i < n * numChannels; i++) + { + convert[i] = samples[i]; + } + // put the converted samples into SoundTouch + sth->pst->putSamples(convert, n); + + numSamples -= n; + samples += n * numChannels; + } +} + /// Clears all the samples in the object's output and internal processing /// buffers. SOUNDTOUCHDLL_API void __cdecl soundtouch_clear(HANDLE h) @@ -254,11 +295,11 @@ SOUNDTOUCHDLL_API void __cdecl soundtouch_clear(HANDLE h) /// Changes a setting controlling the processing system behaviour. See the /// 'SETTING_...' defines for available setting ID's. /// -/// \return 'TRUE' if the setting was succesfully changed -SOUNDTOUCHDLL_API BOOL __cdecl soundtouch_setSetting(HANDLE h, - int settingId, ///< Setting ID number. see SETTING_... defines. - int value ///< New setting value. - ) +/// \return 'nonzero' if the setting was succesfully changed +SOUNDTOUCHDLL_API int __cdecl soundtouch_setSetting(HANDLE h, + int settingId, ///< Setting ID number. see SETTING_... defines. + int value ///< New setting value. + ) { STHANDLE *sth = (STHANDLE*)h; if (sth->dwMagic != STMAGIC) return FALSE; @@ -271,8 +312,8 @@ SOUNDTOUCHDLL_API BOOL __cdecl soundtouch_setSetting(HANDLE h, /// /// \return the setting value. SOUNDTOUCHDLL_API int __cdecl soundtouch_getSetting(HANDLE h, - int settingId ///< Setting ID number, see SETTING_... defines. - ) + int settingId ///< Setting ID number, see SETTING_... defines. + ) { STHANDLE *sth = (STHANDLE*)h; if (sth->dwMagic != STMAGIC) return -1; @@ -297,9 +338,9 @@ SOUNDTOUCHDLL_API uint __cdecl soundtouch_numUnprocessedSamples(HANDLE h) /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly /// with 'ptrBegin' function. SOUNDTOUCHDLL_API uint __cdecl soundtouch_receiveSamples(HANDLE h, - SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples. - uint maxSamples ///< How many samples to receive at max. - ) + SAMPLETYPE *outBuffer, ///< Buffer where to copy output samples. + unsigned int maxSamples ///< How many samples to receive at max. + ) { STHANDLE *sth = (STHANDLE*)h; if (sth->dwMagic != STMAGIC) return 0; @@ -314,6 +355,58 @@ SOUNDTOUCHDLL_API uint __cdecl soundtouch_receiveSamples(HANDLE h, } } +/// int16 version of soundtouch_receiveSamples(): This converts internal float samples +/// into int16 (short) return data type +SOUNDTOUCHDLL_API uint __cdecl soundtouch_receiveSamples_i16(HANDLE h, + short *outBuffer, ///< Buffer where to copy output samples. + unsigned int maxSamples ///< How many samples to receive at max. + ) +{ + STHANDLE *sth = (STHANDLE*)h; + if (sth->dwMagic != STMAGIC) return 0; + uint outTotal = 0; + + if (outBuffer == NULL) + { + // only reduce sample count, not receive samples + return sth->pst->receiveSamples(maxSamples); + } + + uint numChannels = sth->pst->numChannels(); + + // iterate until all samples converted & put to SoundTouch object + while (maxSamples > 0) + { + float convert[8192]; // allocate temporary conversion buffer from stack + + // how many multichannel samples fit into 'convert' buffer: + uint convSamples = 8192 / numChannels; + + // request max 'nround' values at a time to guarantee that these fit in the 'convert' buffer + uint n = (maxSamples > convSamples) ? convSamples : maxSamples; + + uint out = sth->pst->receiveSamples(convert, n); + + // convert & saturate received samples to int16 + for (uint i = 0; i < out * numChannels; i++) + { + // first convert value to int32, then saturate to int16 min/max limits + int value = (int)convert[i]; + value = (value < SHRT_MIN) ? SHRT_MIN : (value > SHRT_MAX) ? SHRT_MAX : value; + outBuffer[i] = (short)value; + } + outTotal += out; + if (out < n) break; // didn't get as many as asked => no more samples available => break here + + maxSamples -= n; + outBuffer += out * numChannels; + } + + // return number of processed samples + return outTotal; +} + + /// Returns number of samples currently available. SOUNDTOUCHDLL_API uint __cdecl soundtouch_numSamples(HANDLE h) { diff --git a/source/SoundTouchDLL/SoundTouchDLL.h b/source/SoundTouchDLL/SoundTouchDLL.h index a384dfe..9bd3d2c 100644 --- a/source/SoundTouchDLL/SoundTouchDLL.h +++ b/source/SoundTouchDLL/SoundTouchDLL.h @@ -102,7 +102,7 @@ SOUNDTOUCHDLL_API void __cdecl soundtouch_setPitchOctaves(HANDLE h, float newPit SOUNDTOUCHDLL_API void __cdecl soundtouch_setPitchSemiTones(HANDLE h, float newPitch); -/// Sets the number of channels, 1 = mono, 2 = stereo +/// Sets the number of channels, 1 = mono, 2 = stereo, n = multichannel SOUNDTOUCHDLL_API void __cdecl soundtouch_setChannels(HANDLE h, unsigned int numChannels); /// Sets sample rate. @@ -122,10 +122,20 @@ SOUNDTOUCHDLL_API void __cdecl soundtouch_flush(HANDLE h); /// calling this function, otherwise throws a runtime_error exception. SOUNDTOUCHDLL_API void __cdecl soundtouch_putSamples(HANDLE h, const float *samples, ///< Pointer to sample buffer. - unsigned int numSamples ///< Number of samples in buffer. Notice - ///< that in case of stereo-sound a single sample - ///< contains data for both channels. - ); + unsigned int numSamples ///< Number of sample frames in buffer. Notice + ///< that in case of multi-channel sound a single + ///< sample frame contains data for all channels. +); + +/// int16 version of soundtouch_putSamples(): This accept int16 (short) sample data +/// and internally converts it to float format before processing +SOUNDTOUCHDLL_API void __cdecl soundtouch_putSamples_i16(HANDLE h, + const short *samples, ///< Pointer to sample buffer. + unsigned int numSamples ///< Number of sample frames in buffer. Notice + ///< that in case of multi-channel sound a single + ///< sample frame contains data for all channels. +); + /// Clears all the samples in the object's output and internal processing /// buffers. @@ -134,19 +144,19 @@ SOUNDTOUCHDLL_API void __cdecl soundtouch_clear(HANDLE h); /// Changes a setting controlling the processing system behaviour. See the /// 'SETTING_...' defines for available setting ID's. /// -/// \return 'TRUE' if the setting was succesfully changed -SOUNDTOUCHDLL_API BOOL __cdecl soundtouch_setSetting(HANDLE h, - int settingId, ///< Setting ID number. see SETTING_... defines. - int value ///< New setting value. - ); +/// \return 'nonzero' if the setting was succesfully changed, otherwise zero +SOUNDTOUCHDLL_API int __cdecl soundtouch_setSetting(HANDLE h, + int settingId, ///< Setting ID number. see SETTING_... defines. + int value ///< New setting value. +); /// Reads a setting controlling the processing system behaviour. See the /// 'SETTING_...' defines for available setting ID's. /// /// \return the setting value. SOUNDTOUCHDLL_API int __cdecl soundtouch_getSetting(HANDLE h, - int settingId ///< Setting ID number, see SETTING_... defines. - ); + int settingId ///< Setting ID number, see SETTING_... defines. +); /// Returns number of samples currently unprocessed. @@ -158,9 +168,17 @@ SOUNDTOUCHDLL_API unsigned int __cdecl soundtouch_numUnprocessedSamples(HANDLE h /// Used to reduce the number of samples in the buffer when accessing the sample buffer directly /// with 'ptrBegin' function. SOUNDTOUCHDLL_API unsigned int __cdecl soundtouch_receiveSamples(HANDLE h, - float *outBuffer, ///< Buffer where to copy output samples. - unsigned int maxSamples ///< How many samples to receive at max. - ); + float *outBuffer, ///< Buffer where to copy output samples. + unsigned int maxSamples ///< How many samples to receive at max. +); + + +/// int16 version of soundtouch_receiveSamples(): This converts internal float samples +/// into int16 (short) return data type +SOUNDTOUCHDLL_API unsigned int __cdecl soundtouch_receiveSamples_i16(HANDLE h, + short *outBuffer, ///< Buffer where to copy output samples. + unsigned int maxSamples ///< How many samples to receive at max. +); /// Returns number of samples currently available. SOUNDTOUCHDLL_API unsigned int __cdecl soundtouch_numSamples(HANDLE h);