Windows: SoundStretch to accept wide-character command line attributes to support asian/non-latin files names.

This commit is contained in:
Olli Parviainen 2024-02-11 15:27:45 +02:00
parent 74514f5597
commit 375e6ccfe9
13 changed files with 485 additions and 488 deletions

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.1) cmake_minimum_required(VERSION 3.1)
project(SoundTouch VERSION 2.3.2 LANGUAGES CXX) project(SoundTouch VERSION 2.3.3 LANGUAGES CXX)
include(GNUInstallDirs) include(GNUInstallDirs)

View File

@ -16,7 +16,7 @@
<body class="normal"> <body class="normal">
<hr> <hr>
<h1>SoundTouch audio processing library v2.3.2</h1> <h1>SoundTouch audio processing library v2.3.2</h1>
<p class="normal">SoundTouch library Copyright &copy; Olli Parviainen 2001-2022</p> <p class="normal">SoundTouch library Copyright &copy; 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
@ -450,7 +450,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-2022</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
@ -874,6 +874,10 @@
<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>
<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>

View File

@ -72,10 +72,10 @@ namespace soundtouch
{ {
/// Soundtouch library version string /// Soundtouch library version string
#define SOUNDTOUCH_VERSION "2.3.2" #define SOUNDTOUCH_VERSION "2.3.3"
/// SoundTouch library version id /// SoundTouch library version id
#define SOUNDTOUCH_VERSION_ID (20302) #define SOUNDTOUCH_VERSION_ID (20303)
// //
// Available setting IDs for the 'setSetting' & 'get_setting' functions: // Available setting IDs for the 'setSetting' & 'get_setting' functions:

View File

@ -30,12 +30,15 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <string> #include <string>
#include <stdlib.h> #include <cstdlib>
#include "RunParameters.h" #include "RunParameters.h"
using namespace std; using namespace std;
namespace soundstretch
{
// Program usage instructions // Program usage instructions
static const char licenseText[] = static const char licenseText[] =
@ -94,9 +97,8 @@ static int _toLowerCase(int c)
return c; return c;
} }
// Constructor // Constructor
RunParameters::RunParameters(const int nParams, const char * const paramStr[]) RunParameters::RunParameters(int nParams, const CHARTYPE* paramStr[])
{ {
int i; int i;
int nFirstParam; int nFirstParam;
@ -112,28 +114,17 @@ RunParameters::RunParameters(const int nParams, const char * const paramStr[])
} }
string msg = whatText; string msg = whatText;
msg += usage; msg += usage;
ST_THROW_RT_ERROR(msg.c_str()); throw(msg);
} }
inFileName = nullptr;
outFileName = nullptr;
tempoDelta = 0;
pitchDelta = 0;
rateDelta = 0;
quick = 0;
noAntiAlias = 0;
goalBPM = 0;
speech = false;
detectBPM = false;
// Get input & output file names // Get input & output file names
inFileName = (char*)paramStr[1]; inFileName = paramStr[1];
outFileName = (char*)paramStr[2]; outFileName = paramStr[2];
if (outFileName[0] == '-') if (outFileName[0] == '-')
{ {
// no outputfile name was given but parameters // outputfile name was omitted but other parameter switches given instead
outFileName = nullptr; outFileName = STRING_CONST("");
nFirstParam = 2; nFirstParam = 2;
} }
else else
@ -182,25 +173,33 @@ void RunParameters::checkLimits()
} }
} }
// Convert STRING to std::string. Actually needed only if STRING is std::wstring, but conversion penalty is negligible
// Unknown switch parameter -- throws an exception with an error message std::string convertString(const STRING& str)
void RunParameters::throwIllegalParamExp(const string &str) const
{ {
string msg = "ERROR : Illegal parameter \""; std::string res;
msg += str; for (auto c : str)
msg += "\".\n\n"; {
msg += usage; res += (char)c;
ST_THROW_RT_ERROR(msg.c_str()); }
return res;
} }
// Unknown switch parameter -- throws an exception with an error message
void RunParameters::throwIllegalParamExp(const STRING &str) const
{
string msg = "ERROR : Illegal parameter \"";
msg += convertString(str);
msg += "\".\n\n";
msg += usage;
ST_THROW_RT_ERROR(msg);
}
void RunParameters::throwLicense() const void RunParameters::throwLicense() const
{ {
ST_THROW_RT_ERROR(licenseText); ST_THROW_RT_ERROR(licenseText);
} }
float RunParameters::parseSwitchValue(const STRING& str) const
float RunParameters::parseSwitchValue(const string &str) const
{ {
int pos; int pos;
@ -212,14 +211,14 @@ float RunParameters::parseSwitchValue(const string &str) const
} }
// Read numerical parameter value after '=' // Read numerical parameter value after '='
return (float)atof(str.substr(pos + 1).c_str()); return (float)stof(str.substr(pos + 1).c_str());
} }
// Interprets a single switch parameter string of format "-switch=xx" // Interprets a single switch parameter string of format "-switch=xx"
// Valid switches are "-tempo=xx", "-pitch=xx" and "-rate=xx". Stores // Valid switches are "-tempo=xx", "-pitch=xx" and "-rate=xx". Stores
// switch values into 'params' structure. // switch values into 'params' structure.
void RunParameters::parseSwitchParam(const string &str) void RunParameters::parseSwitchParam(const STRING& str)
{ {
int upS; int upS;
@ -289,3 +288,5 @@ void RunParameters::parseSwitchParam(const string &str)
throwIllegalParamExp(str); throwIllegalParamExp(str);
} }
} }
}

View File

@ -32,34 +32,39 @@
#ifndef RUNPARAMETERS_H #ifndef RUNPARAMETERS_H
#define RUNPARAMETERS_H #define RUNPARAMETERS_H
#include "STTypes.h"
#include <string> #include <string>
#include "STTypes.h"
#include "SS_CharTypes.h"
#include "WavFile.h"
using namespace std; namespace soundstretch
{
/// Parses command line parameters into program parameters /// Parses command line parameters into program parameters
class RunParameters class RunParameters
{ {
private: private:
void throwIllegalParamExp(const string &str) const; void throwIllegalParamExp(const STRING& str) const;
void throwLicense() const; void throwLicense() const;
void parseSwitchParam(const string &str); void parseSwitchParam(const STRING& str);
void checkLimits(); void checkLimits();
float parseSwitchValue(const string &str) const; float parseSwitchValue(const STRING& tr) const;
public: public:
char *inFileName; STRING inFileName;
char *outFileName; STRING outFileName;
float tempoDelta; float tempoDelta{ 0 };
float pitchDelta; float pitchDelta{ 0 };
float rateDelta; float rateDelta{ 0 };
int quick; int quick{ 0 };
int noAntiAlias; int noAntiAlias{ 0 };
float goalBPM; float goalBPM{ 0 };
bool detectBPM; bool detectBPM{ false };
bool speech; bool speech{ false };
RunParameters(const int nParams, const char * const paramStr[]); RunParameters(int nParams, const CHARTYPE* paramStr[]);
}; };
}
#endif #endif

View File

@ -42,14 +42,23 @@
#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 ";
@ -169,17 +178,13 @@ 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 == nullptr) 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();
@ -192,9 +197,7 @@ WavInFile::WavInFile(FILE *file)
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();
@ -213,7 +216,6 @@ void WavInFile::init()
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");
} }
@ -223,7 +225,6 @@ void WavInFile::init()
(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.");
} }
@ -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 == nullptr) 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);
@ -727,8 +724,7 @@ WavOutFile::WavOutFile(FILE *file, int sampleRate, int bits, int channels)
fptr = file; fptr = file;
if (fptr == nullptr) 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);
@ -875,7 +871,7 @@ void WavOutFile::write(const short *buffer, int numElems)
// use temp buffer to swap byte order if necessary // use temp buffer to swap byte order if necessary
short* pTemp = (short*)getConvBuffer(numElems * sizeof(short)); short* pTemp = (short*)getConvBuffer(numElems * sizeof(short));
memcpy(pTemp, buffer, numElems * 2); memcpy(pTemp, buffer, (size_t)numElems * 2L);
_swap16Buffer(pTemp, numElems); _swap16Buffer(pTemp, numElems);
res = (int)fwrite(pTemp, 2, numElems, fptr); res = (int)fwrite(pTemp, 2, numElems, fptr);
@ -984,3 +980,5 @@ void WavOutFile::write(const float *buffer, int numElems)
} }
bytesWritten += numBytes; bytesWritten += numBytes;
} }
}

View File

@ -40,7 +40,12 @@
#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;
@ -145,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);
@ -241,7 +246,7 @@ private:
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)
@ -271,4 +276,6 @@ public:
); );
}; };
}
#endif #endif

View File

@ -29,10 +29,12 @@
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <memory>
#include <stdexcept> #include <stdexcept>
#include <stdio.h> #include <string>
#include <string.h> #include <cstdio>
#include <time.h> #include <ctime>
#include "RunParameters.h" #include "RunParameters.h"
#include "WavFile.h" #include "WavFile.h"
#include "SoundTouch.h" #include "SoundTouch.h"
@ -41,6 +43,9 @@
using namespace soundtouch; using namespace soundtouch;
using namespace std; using namespace std;
namespace soundstretch
{
// Processing chunk size (size chosen to be divisible by 2, 4, 6, 8, 10, 12, 14, 16 channels ...) // Processing chunk size (size chosen to be divisible by 2, 4, 6, 8, 10, 12, 14, 16 channels ...)
#define BUFF_SIZE 6720 #define BUFF_SIZE 6720
@ -68,76 +73,67 @@ static const char _helloText[] =
"more information.\n" "more information.\n"
"\n"; "\n";
static void openFiles(WavInFile **inFile, WavOutFile **outFile, const RunParameters *params) static void openFiles(unique_ptr<WavInFile>& inFile, unique_ptr<WavOutFile>& outFile, const RunParameters& params)
{ {
int bits, samplerate, channels; if (params.inFileName == STRING_CONST("stdin"))
if (strcmp(params->inFileName, "stdin") == 0)
{ {
// used 'stdin' as input file // used 'stdin' as input file
SET_STREAM_TO_BIN_MODE(stdin); SET_STREAM_TO_BIN_MODE(stdin);
*inFile = new WavInFile(stdin); inFile = make_unique<WavInFile>(stdin);
} }
else else
{ {
// open input file... // open input file...
*inFile = new WavInFile(params->inFileName); inFile = make_unique<WavInFile>(params.inFileName.c_str());
} }
// ... open output file with same sound parameters // ... open output file with same sound parameters
bits = (int)(*inFile)->getNumBits(); const int bits = (int)inFile->getNumBits();
samplerate = (int)(*inFile)->getSampleRate(); const int samplerate = (int)inFile->getSampleRate();
channels = (int)(*inFile)->getNumChannels(); const int channels = (int)inFile->getNumChannels();
if (params->outFileName) if (!params.outFileName.empty())
{ {
if (strcmp(params->outFileName, "stdout") == 0) if (params.outFileName == STRING_CONST("stdout"))
{ {
SET_STREAM_TO_BIN_MODE(stdout); SET_STREAM_TO_BIN_MODE(stdout);
*outFile = new WavOutFile(stdout, samplerate, bits, channels); outFile = make_unique<WavOutFile>(stdout, samplerate, bits, channels);
} }
else else
{ {
*outFile = new WavOutFile(params->outFileName, samplerate, bits, channels); outFile = make_unique<WavOutFile>(params.outFileName.c_str(), samplerate, bits, channels);
} }
} }
else
{
*outFile = nullptr;
}
} }
// Sets the 'SoundTouch' object up according to input file sound format & // Sets the 'SoundTouch' object up according to input file sound format &
// command line parameters // command line parameters
static void setup(SoundTouch *pSoundTouch, const WavInFile *inFile, const RunParameters *params) static void setup(SoundTouch& soundTouch, const WavInFile& inFile, const RunParameters& params)
{ {
int sampleRate; const int sampleRate = (int)inFile.getSampleRate();
int channels; const int channels = (int)inFile.getNumChannels();
soundTouch.setSampleRate(sampleRate);
soundTouch.setChannels(channels);
sampleRate = (int)inFile->getSampleRate(); soundTouch.setTempoChange(params.tempoDelta);
channels = (int)inFile->getNumChannels(); soundTouch.setPitchSemiTones(params.pitchDelta);
pSoundTouch->setSampleRate(sampleRate); soundTouch.setRateChange(params.rateDelta);
pSoundTouch->setChannels(channels);
pSoundTouch->setTempoChange(params->tempoDelta); soundTouch.setSetting(SETTING_USE_QUICKSEEK, params.quick);
pSoundTouch->setPitchSemiTones(params->pitchDelta); soundTouch.setSetting(SETTING_USE_AA_FILTER, !(params.noAntiAlias));
pSoundTouch->setRateChange(params->rateDelta);
pSoundTouch->setSetting(SETTING_USE_QUICKSEEK, params->quick); if (params.speech)
pSoundTouch->setSetting(SETTING_USE_AA_FILTER, !(params->noAntiAlias));
if (params->speech)
{ {
// use settings for speech processing // use settings for speech processing
pSoundTouch->setSetting(SETTING_SEQUENCE_MS, 40); soundTouch.setSetting(SETTING_SEQUENCE_MS, 40);
pSoundTouch->setSetting(SETTING_SEEKWINDOW_MS, 15); soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 15);
pSoundTouch->setSetting(SETTING_OVERLAP_MS, 8); soundTouch.setSetting(SETTING_OVERLAP_MS, 8);
fprintf(stderr, "Tune processing parameters for speech processing.\n"); fprintf(stderr, "Tune processing parameters for speech processing.\n");
} }
// print processing information // print processing information
if (params->outFileName) if (!params.outFileName.empty())
{ {
#ifdef SOUNDTOUCH_INTEGER_SAMPLES #ifdef SOUNDTOUCH_INTEGER_SAMPLES
fprintf(stderr, "Uses 16bit integer sample type in processing.\n\n"); fprintf(stderr, "Uses 16bit integer sample type in processing.\n\n");
@ -149,9 +145,9 @@ static void setup(SoundTouch *pSoundTouch, const WavInFile *inFile, const RunPar
#endif #endif
// print processing information only if outFileName given i.e. some processing will happen // print processing information only if outFileName given i.e. some processing will happen
fprintf(stderr, "Processing the file with the following changes:\n"); fprintf(stderr, "Processing the file with the following changes:\n");
fprintf(stderr, " tempo change = %+g %%\n", params->tempoDelta); fprintf(stderr, " tempo change = %+g %%\n", params.tempoDelta);
fprintf(stderr, " pitch change = %+g semitones\n", params->pitchDelta); fprintf(stderr, " pitch change = %+g semitones\n", params.pitchDelta);
fprintf(stderr, " rate change = %+g %%\n\n", params->rateDelta); fprintf(stderr, " rate change = %+g %%\n\n", params.rateDelta);
fprintf(stderr, "Working..."); fprintf(stderr, "Working...");
} }
else else
@ -165,30 +161,24 @@ static void setup(SoundTouch *pSoundTouch, const WavInFile *inFile, const RunPar
// Processes the sound // Processes the sound
static void process(SoundTouch *pSoundTouch, WavInFile *inFile, WavOutFile *outFile) static void process(SoundTouch& soundTouch, WavInFile& inFile, WavOutFile& outFile)
{ {
int nSamples;
int nChannels;
int buffSizeSamples;
SAMPLETYPE sampleBuffer[BUFF_SIZE]; SAMPLETYPE sampleBuffer[BUFF_SIZE];
int nSamples;
if ((inFile == nullptr) || (outFile == nullptr)) return; // nothing to do. const int nChannels = (int)inFile.getNumChannels();
nChannels = (int)inFile->getNumChannels();
assert(nChannels > 0); assert(nChannels > 0);
buffSizeSamples = BUFF_SIZE / nChannels; const int buffSizeSamples = BUFF_SIZE / nChannels;
// Process samples read from the input file // Process samples read from the input file
while (inFile->eof() == 0) while (inFile.eof() == 0)
{ {
int num;
// Read a chunk of samples from the input file // Read a chunk of samples from the input file
num = inFile->read(sampleBuffer, BUFF_SIZE); const int num = inFile.read(sampleBuffer, BUFF_SIZE);
nSamples = num / (int)inFile->getNumChannels(); int nSamples = num / (int)inFile.getNumChannels();
// Feed the samples into SoundTouch processor // Feed the samples into SoundTouch processor
pSoundTouch->putSamples(sampleBuffer, nSamples); soundTouch.putSamples(sampleBuffer, nSamples);
// Read ready samples from SoundTouch processor & write them output file. // Read ready samples from SoundTouch processor & write them output file.
// NOTES: // NOTES:
@ -200,57 +190,53 @@ static void process(SoundTouch *pSoundTouch, WavInFile *inFile, WavOutFile *outF
// outputs samples. // outputs samples.
do do
{ {
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples); nSamples = soundTouch.receiveSamples(sampleBuffer, buffSizeSamples);
outFile->write(sampleBuffer, nSamples * nChannels); outFile.write(sampleBuffer, nSamples * nChannels);
} while (nSamples != 0); } while (nSamples != 0);
} }
// Now the input file is processed, yet 'flush' few last samples that are // Now the input file is processed, yet 'flush' few last samples that are
// hiding in the SoundTouch's internal processing pipeline. // hiding in the SoundTouch's internal processing pipeline.
pSoundTouch->flush(); soundTouch.flush();
do do
{ {
nSamples = pSoundTouch->receiveSamples(sampleBuffer, buffSizeSamples); nSamples = soundTouch.receiveSamples(sampleBuffer, buffSizeSamples);
outFile->write(sampleBuffer, nSamples * nChannels); outFile.write(sampleBuffer, nSamples * nChannels);
} while (nSamples != 0); } while (nSamples != 0);
} }
// Detect BPM rate of inFile and adjust tempo setting accordingly if necessary // Detect BPM rate of inFile and adjust tempo setting accordingly if necessary
static void detectBPM(WavInFile *inFile, RunParameters *params) static void detectBPM(WavInFile& inFile, RunParameters& params)
{ {
float bpmValue; BPMDetect bpm(inFile.getNumChannels(), inFile.getSampleRate());
int nChannels;
BPMDetect bpm(inFile->getNumChannels(), inFile->getSampleRate());
SAMPLETYPE sampleBuffer[BUFF_SIZE]; SAMPLETYPE sampleBuffer[BUFF_SIZE];
// detect bpm rate // detect bpm rate
fprintf(stderr, "Detecting BPM rate..."); fprintf(stderr, "Detecting BPM rate...");
fflush(stderr); fflush(stderr);
nChannels = (int)inFile->getNumChannels(); const int nChannels = (int)inFile.getNumChannels();
int readSize = BUFF_SIZE - BUFF_SIZE % nChannels; // round read size down to multiple of num.channels int readSize = BUFF_SIZE - BUFF_SIZE % nChannels; // round read size down to multiple of num.channels
// Process the 'inFile' in small blocks, repeat until whole file has // Process the 'inFile' in small blocks, repeat until whole file has
// been processed // been processed
while (inFile->eof() == 0) while (inFile.eof() == 0)
{ {
int num, samples;
// Read sample data from input file // Read sample data from input file
num = inFile->read(sampleBuffer, readSize); const int num = inFile.read(sampleBuffer, readSize);
// Enter the new samples to the bpm analyzer class // Enter the new samples to the bpm analyzer class
samples = num / nChannels; const int samples = num / nChannels;
bpm.inputSamples(sampleBuffer, samples); bpm.inputSamples(sampleBuffer, samples);
} }
// Now the whole song data has been analyzed. Read the resulting bpm. // Now the whole song data has been analyzed. Read the resulting bpm.
bpmValue = bpm.getBpm(); const float bpmValue = bpm.getBpm();
fprintf(stderr, "Done!\n"); fprintf(stderr, "Done!\n");
// rewind the file after bpm detection // rewind the file after bpm detection
inFile->rewind(); inFile.rewind();
if (bpmValue > 0) if (bpmValue > 0)
{ {
@ -262,61 +248,64 @@ static void detectBPM(WavInFile *inFile, RunParameters *params)
return; return;
} }
if (params->goalBPM > 0) if (params.goalBPM > 0)
{ {
// adjust tempo to given bpm // adjust tempo to given bpm
params->tempoDelta = (params->goalBPM / bpmValue - 1.0f) * 100.0f; params.tempoDelta = (params.goalBPM / bpmValue - 1.0f) * 100.0f;
fprintf(stderr, "The file will be converted to %.1f BPM\n\n", params->goalBPM); fprintf(stderr, "The file will be converted to %.1f BPM\n\n", params.goalBPM);
} }
} }
void ss_main(RunParameters& params)
int main(const int nParams, const char * const paramStr[])
{ {
WavInFile *inFile; unique_ptr<WavInFile> inFile;
WavOutFile *outFile; unique_ptr<WavOutFile> outFile;
RunParameters *params;
SoundTouch soundTouch; SoundTouch soundTouch;
fprintf(stderr, _helloText, SoundTouch::getVersionString()); fprintf(stderr, _helloText, soundTouch.getVersionString());
try
{
// Parse command line parameters
params = new RunParameters(nParams, paramStr);
// Open input & output files // Open input & output files
openFiles(&inFile, &outFile, params); openFiles(inFile, outFile, params);
if (params->detectBPM == true) if (params.detectBPM == true)
{ {
// detect sound BPM (and adjust processing parameters // detect sound BPM (and adjust processing parameters
// accordingly if necessary) // accordingly if necessary)
detectBPM(inFile, params); detectBPM(*inFile, params);
} }
// Setup the 'SoundTouch' object for processing the sound // Setup the 'SoundTouch' object for processing the sound
setup(&soundTouch, inFile, params); setup(soundTouch, *inFile, params);
// clock_t cs = clock(); // for benchmarking processing duration // clock_t cs = clock(); // for benchmarking processing duration
// Process the sound // Process the sound
process(&soundTouch, inFile, outFile); if (inFile && outFile)
{
process(soundTouch, *inFile, *outFile);
}
// clock_t ce = clock(); // for benchmarking processing duration // clock_t ce = clock(); // for benchmarking processing duration
// printf("duration: %lf\n", (double)(ce-cs)/CLOCKS_PER_SEC); // printf("duration: %lf\n", (double)(ce-cs)/CLOCKS_PER_SEC);
// Close WAV file handles & dispose of the objects
delete inFile;
delete outFile;
delete params;
fprintf(stderr, "Done!\n"); fprintf(stderr, "Done!\n");
} }
}
#if _WIN32
int wmain(int argc, const wchar_t* args[])
#else
int main(int argc, const char* args[])
#endif
{
try
{
soundstretch::RunParameters params(argc, args);
soundstretch::ss_main(params);
}
catch (const runtime_error& e) catch (const runtime_error& e)
{ {
// An exception occurred during processing, display an error message
fprintf(stderr, "%s\n", e.what()); fprintf(stderr, "%s\n", e.what());
return -1; return -1;
} }
return 0; return 0;
} }

View File

@ -28,25 +28,25 @@
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v142</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<UseOfMfc>false</UseOfMfc> <UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v142</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<UseOfMfc>false</UseOfMfc> <UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v142</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<UseOfMfc>false</UseOfMfc> <UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<PlatformToolset>v142</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<UseOfMfc>false</UseOfMfc> <UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">
@ -134,10 +134,7 @@
<GenerateMapFile>true</GenerateMapFile> <GenerateMapFile>true</GenerateMapFile>
<MapFileName>$(OutDir)$(TargetName).map</MapFileName> <MapFileName>$(OutDir)$(TargetName).map</MapFileName>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention /> <DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
<Command>if not exist ..\..\bin mkdir ..\..\bin <Command>if not exist ..\..\bin mkdir ..\..\bin
@ -183,9 +180,7 @@ copy $(OutDir)$(TargetName)$(TargetExt) ..\..\bin\</Command>
<GenerateMapFile>true</GenerateMapFile> <GenerateMapFile>true</GenerateMapFile>
<MapFileName>$(OutDir)$(TargetName).map</MapFileName> <MapFileName>$(OutDir)$(TargetName).map</MapFileName>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention /> <DataExecutionPrevention />
<TargetMachine>MachineX86</TargetMachine>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
<Command>if not exist ..\..\bin mkdir ..\..\bin <Command>if not exist ..\..\bin mkdir ..\..\bin
@ -234,9 +229,7 @@ copy $(OutDir)$(TargetName)$(TargetExt) ..\..\bin\</Command>
<GenerateMapFile>true</GenerateMapFile> <GenerateMapFile>true</GenerateMapFile>
<MapFileName>$(OutDir)$(TargetName).map</MapFileName> <MapFileName>$(OutDir)$(TargetName).map</MapFileName>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention /> <DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
<Command>if not exist ..\..\bin mkdir ..\..\bin <Command>if not exist ..\..\bin mkdir ..\..\bin
@ -284,9 +277,7 @@ copy $(OutDir)$(TargetName)$(TargetExt) ..\..\bin\</Command>
<GenerateMapFile>true</GenerateMapFile> <GenerateMapFile>true</GenerateMapFile>
<MapFileName>$(OutDir)$(TargetName).map</MapFileName> <MapFileName>$(OutDir)$(TargetName).map</MapFileName>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<DataExecutionPrevention /> <DataExecutionPrevention />
<TargetMachine>MachineX64</TargetMachine>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
<Command>if not exist ..\..\bin mkdir ..\..\bin <Command>if not exist ..\..\bin mkdir ..\..\bin
@ -327,6 +318,7 @@ copy $(OutDir)$(TargetName)$(TargetExt) ..\..\bin\</Command>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="RunParameters.h" /> <ClInclude Include="RunParameters.h" />
<ClInclude Include="SS_CharTypes.h" />
<ClInclude Include="WavFile.h" /> <ClInclude Include="WavFile.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -27,25 +27,25 @@
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<UseOfMfc>false</UseOfMfc> <UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<UseOfMfc>false</UseOfMfc> <UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<UseOfMfc>false</UseOfMfc> <UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<UseOfMfc>false</UseOfMfc> <UseOfMfc>false</UseOfMfc>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">

View File

@ -16,9 +16,10 @@
#include "../../SoundStretch/WavFile.h" #include "../../SoundStretch/WavFile.h"
using namespace std; using namespace std;
using namespace soundstretch;
// DllTest main // DllTest main
int main(int argc, char *argv[]) int wmain(int argc, const wchar_t *argv[])
{ {
// Check program arguments // Check program arguments
if (argc < 4) if (argc < 4)
@ -27,22 +28,22 @@ int main(int argc, char *argv[])
return -1; return -1;
} }
const char *inFileName = argv[1]; wstring inFileName = argv[1];
const char *outFileName = argv[2]; wstring outFileName = argv[2];
string str_sampleType = argv[3]; wstring str_sampleType = argv[3];
bool floatSample; bool floatSample;
if (str_sampleType.compare("float") == 0) if (str_sampleType == L"float")
{ {
floatSample = true; floatSample = true;
} }
else if (str_sampleType.compare("short") == 0) else if (str_sampleType == L"short")
{ {
floatSample = false; floatSample = false;
} }
else else
{ {
cerr << "Missing or invalid sampletype '" << str_sampleType << "'. Expected either short or float" << endl; cerr << "Missing or invalid sampletype. Expected either short or float" << endl;
return -1; return -1;
} }

View File

@ -69,12 +69,12 @@ BEGIN
BEGIN BEGIN
VALUE "Comments", "SoundTouch Library licensed for 3rd party applications subject to LGPL license v2.1. Visit http://www.surina.net/soundtouch for more information about the SoundTouch library." VALUE "Comments", "SoundTouch Library licensed for 3rd party applications subject to LGPL license v2.1. Visit http://www.surina.net/soundtouch for more information about the SoundTouch library."
VALUE "FileDescription", "SoundTouch Dynamic Link Library" VALUE "FileDescription", "SoundTouch Dynamic Link Library"
VALUE "FileVersion", "2.3.2.0" VALUE "FileVersion", "2.3.3.0"
VALUE "InternalName", "SoundTouch" VALUE "InternalName", "SoundTouch"
VALUE "LegalCopyright", "Copyright (C) Olli Parviainen 2023" VALUE "LegalCopyright", "Copyright (C) Olli Parviainen 2024"
VALUE "OriginalFilename", "SoundTouch.dll" VALUE "OriginalFilename", "SoundTouch.dll"
VALUE "ProductName", " SoundTouch Dynamic Link Library" VALUE "ProductName", " SoundTouch Dynamic Link Library"
VALUE "ProductVersion", "2.3.2.0" VALUE "ProductVersion", "2.3.3.0"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -27,22 +27,22 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v142</PlatformToolset> <PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings"> <ImportGroup Label="ExtensionSettings">