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,17 +42,26 @@
#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";
@ -66,67 +75,67 @@ static const char dataStr[] = "data";
// 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 >> 8) & 0x0000FF00) |
((dwData << 8) & 0x00FF0000) |
((dwData << 24) & 0xFF000000);
return dwData;
}
// helper-function to swap byte-order of 16bit integer
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++)
{ {
dwData = ((dwData >> 24) & 0x000000FF) | pData[i] = _swap16(pData[i]);
((dwData >> 8) & 0x0000FF00) |
((dwData << 8) & 0x00FF0000) |
((dwData << 24) & 0xFF000000);
return dwData;
}
// helper-function to swap byte-order of 16bit integer
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 ++)
{
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 *, int) static inline void _swap16Buffer(short*, int)
{ {
// do nothing // do nothing
} }
#endif // BIG_ENDIAN #endif // BIG_ENDIAN
@ -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 == 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();
} }
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();
@ -213,17 +216,15 @@ 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");
} }
// 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.");
} }
@ -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;
@ -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,53 +299,53 @@ 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;
@ -353,7 +354,7 @@ int WavInFile::read(short *buffer, int maxElems)
/// 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;
@ -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;
@ -462,7 +463,7 @@ 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;
@ -470,7 +471,7 @@ static int isAlphaStr(const char *str)
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
@ -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)
@ -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 == 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);
@ -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 == 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);
@ -801,17 +797,17 @@ 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);
@ -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;
@ -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());
} }
} }
} }
@ -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,56 +920,56 @@ 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);
@ -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,18 +43,21 @@
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
#if _WIN32 #if _WIN32
#include <io.h> #include <io.h>
#include <fcntl.h> #include <fcntl.h>
// Macro for Win32 standard input/output stream support: Sets a file stream into binary mode // Macro for Win32 standard input/output stream support: Sets a file stream into binary mode
#define SET_STREAM_TO_BIN_MODE(f) (_setmode(_fileno(f), _O_BINARY)) #define SET_STREAM_TO_BIN_MODE(f) (_setmode(_fileno(f), _O_BINARY))
#else #else
// Not needed for GNU environment... // Not needed for GNU environment...
#define SET_STREAM_TO_BIN_MODE(f) {} #define SET_STREAM_TO_BIN_MODE(f) {}
#endif #endif
@ -68,90 +73,81 @@ 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");
#else #else
#ifndef SOUNDTOUCH_FLOAT_SAMPLES #ifndef SOUNDTOUCH_FLOAT_SAMPLES
#error "Sampletype not defined" #error "Sampletype not defined"
#endif #endif
fprintf(stderr, "Uses 32bit floating point sample type in processing.\n\n"); fprintf(stderr, "Uses 32bit floating point sample type in processing.\n\n");
#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());
// Open input & output files
openFiles(inFile, outFile, params);
if (params.detectBPM == true)
{
// detect sound BPM (and adjust processing parameters
// accordingly if necessary)
detectBPM(*inFile, params);
}
// Setup the 'SoundTouch' object for processing the sound
setup(soundTouch, *inFile, params);
// clock_t cs = clock(); // for benchmarking processing duration
// Process the sound
if (inFile && outFile)
{
process(soundTouch, *inFile, *outFile);
}
// clock_t ce = clock(); // for benchmarking processing duration
// printf("duration: %lf\n", (double)(ce-cs)/CLOCKS_PER_SEC);
fprintf(stderr, "Done!\n");
}
}
#if _WIN32
int wmain(int argc, const wchar_t* args[])
#else
int main(int argc, const char* args[])
#endif
{
try try
{ {
// Parse command line parameters soundstretch::RunParameters params(argc, args);
params = new RunParameters(nParams, paramStr); soundstretch::ss_main(params);
// Open input & output files
openFiles(&inFile, &outFile, params);
if (params->detectBPM == true)
{
// detect sound BPM (and adjust processing parameters
// accordingly if necessary)
detectBPM(inFile, params);
}
// Setup the 'SoundTouch' object for processing the sound
setup(&soundTouch, inFile, params);
// clock_t cs = clock(); // for benchmarking processing duration
// Process the sound
process(&soundTouch, inFile, outFile);
// clock_t ce = clock(); // for benchmarking processing duration
// 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");
} }
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">