Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/alexmarsev/soundtouch.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoroparviai <oparviai@f3a24b6a-cf45-0410-b55a-8c22e2698227>2012-08-23 00:33:50 +0400
committeroparviai <oparviai@f3a24b6a-cf45-0410-b55a-8c22e2698227>2012-08-23 00:33:50 +0400
commita78f58794ee6c90cc234e99310724205d04cce71 (patch)
tree81e906358f6de217e7e5e109d96d527654ac9451
parent1733fa03359f42fb7bd9ac30b52768b633b8aa8a (diff)
Added wav file support for 24/32 bit samples, as far as data is read into floating point variables.
-rw-r--r--source/SoundStretch/WavFile.cpp294
-rw-r--r--source/SoundStretch/WavFile.h22
2 files changed, 254 insertions, 62 deletions
diff --git a/source/SoundStretch/WavFile.cpp b/source/SoundStretch/WavFile.cpp
index ed5791f..c75a4be 100644
--- a/source/SoundStretch/WavFile.cpp
+++ b/source/SoundStretch/WavFile.cpp
@@ -84,29 +84,31 @@ static const char dataStr[] = "data";
// big-endian CPU, swap bytes in 16 & 32 bit words
// helper-function to swap byte-order of 32bit integer
- static inline void _swap32(unsigned int &dwData)
+ static inline long _swap32(long &dwData)
{
dwData = ((dwData >> 24) & 0x000000FF) |
- ((dwData >> 8) & 0x0000FF00) |
- ((dwData << 8) & 0x00FF0000) |
- ((dwData << 24) & 0xFF000000);
+ ((dwData >> 8) & 0x0000FF00) |
+ ((dwData << 8) & 0x00FF0000) |
+ ((dwData << 24) & 0xFF000000);
+ return dwData;
}
// helper-function to swap byte-order of 16bit integer
- static inline void _swap16(unsigned short &wData)
+ static inline short _swap16(short &wData)
{
wData = ((wData >> 8) & 0x00FF) |
((wData << 8) & 0xFF00);
+ return wData;
}
// helper-function to swap byte-order of buffer of 16bit integers
- static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumWords)
+ static inline void _swap16Buffer(short *pData, int numWords)
{
- unsigned long i;
+ int i;
- for (i = 0; i < dwNumWords; i ++)
+ for (i = 0; i < numWords; i ++)
{
- _swap16(pData[i]);
+ pData[i] = _swap16(pData[i]);
}
}
@@ -114,19 +116,21 @@ static const char dataStr[] = "data";
// little-endian CPU, WAV file is ok as such
// dummy helper-function
- static inline void _swap32(unsigned int &dwData)
+ static inline long _swap32(long &dwData)
{
// do nothing
+ return dwData;
}
// dummy helper-function
- static inline void _swap16(unsigned short &wData)
+ static inline short _swap16(short &wData)
{
// do nothing
+ return wData;
}
// dummy helper-function
- static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumBytes)
+ static inline void _swap16Buffer(short *pData, int numBytes)
{
// do nothing
}
@@ -136,6 +140,39 @@ static const char dataStr[] = "data";
//////////////////////////////////////////////////////////////////////////////
//
+// Class WavFileBase
+//
+
+WavFileBase::WavFileBase()
+{
+ convBuff = NULL;
+ convBuffSize = 0;
+}
+
+
+WavFileBase::~WavFileBase()
+{
+ delete[] convBuff;
+ convBuffSize = 0;
+}
+
+
+/// Get pointer to conversion buffer of at min. given size
+void *WavFileBase::getConvBuffer(int sizeBytes)
+{
+ if (convBuffSize < sizeBytes)
+ {
+ delete[] convBuff;
+
+ convBuffSize = (sizeBytes + 15) & -8;
+ convBuff = new char[convBuffSize];
+ }
+ return convBuff;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
// Class WavInFile
//
@@ -188,11 +225,13 @@ void WavInFile::init()
ST_THROW_RT_ERROR(msg.c_str());
}
+ /* Ignore 'fixed' field value as 32bit signed linear data can have other value than 1.
if (header.format.fixed != 1)
{
string msg = "Input file uses unsupported encoding.";
ST_THROW_RT_ERROR(msg.c_str());
}
+ */
dataRead = 0;
}
@@ -268,7 +307,7 @@ int WavInFile::read(short *buffer, int maxElems)
if (header.format.bits_per_sample == 8)
{
// 8 bit format
- char *temp = new char[maxElems];
+ char *temp = (char*)getConvBuffer(maxElems);
int i;
numElems = read(temp, maxElems);
@@ -277,7 +316,6 @@ int WavInFile::read(short *buffer, int maxElems)
{
buffer[i] = temp[i] << 8;
}
- delete[] temp;
}
else
{
@@ -307,33 +345,106 @@ int WavInFile::read(short *buffer, int maxElems)
numElems = numBytes / 2;
// 16bit samples, swap byte order if necessary
- _swap16Buffer((unsigned short *)buffer, numElems);
+ _swap16Buffer((short *)buffer, numElems);
}
return numElems;
}
-
+/// Read data in float format. Notice that when reading in float format
+/// 8/16/24/32 bit sample formats are supported
int WavInFile::read(float *buffer, int maxElems)
{
- short *temp = new short[maxElems];
- int num;
- int i;
- double fscale;
+ unsigned int afterDataRead;
+ int numBytes;
+ int numElems;
+ int bytesPerSample;
- num = read(temp, maxElems);
+ assert(buffer);
- fscale = 1.0 / 32768.0;
- // convert to floats, scale to range [-1..+1[
- for (i = 0; i < num; i ++)
+ bytesPerSample = header.format.bits_per_sample / 8;
+ if ((bytesPerSample < 1) || (bytesPerSample > 4))
{
- buffer[i] = (float)(fscale * (double)temp[i]);
+ stringstream ss;
+ ss << "\nOnly 8/16/24/32 bit sample WAV files supported. 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());
}
- delete[] temp;
+ numBytes = maxElems * bytesPerSample;
+ 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);
+ }
+
+ // read raw data into temporary buffer
+ char *temp = (char*)getConvBuffer(numBytes);
+ numBytes = fread(temp, 1, numBytes, fptr);
+ dataRead += numBytes;
+
+ numElems = numBytes / bytesPerSample;
+
+ // swap byte ordert & convert to float, depending on sample format
+ switch (bytesPerSample)
+ {
+ case 1:
+ {
+ char *temp2 = temp;
+ double conv = 1.0 / 128;
+ for (int i = 0; i < numElems; i ++)
+ {
+ buffer[i] = (float)(temp2[i] * conv);
+ }
+ break;
+ }
+
+ case 2:
+ {
+ short *temp2 = (short*)temp;
+ double conv = 1.0 / 32768;
+ for (int i = 0; i < numElems; i ++)
+ {
+ short value = temp2[i];
+ buffer[i] = (float)(_swap16(value) * conv);
+ }
+ break;
+ }
+
+ case 3:
+ {
+ char *temp2 = (char *)temp;
+ double conv = 1.0 / 8388608;
+ for (int i = 0; i < numElems; i ++)
+ {
+ long value = *((long*)temp2);
+ value = _swap32(value) & 0x00ffffff; // take 24 bits
+ value |= (value & 0x00800000) ? 0xff000000 : 0; // extend minus sign bits
+ buffer[i] = (float)(value * conv);
+ temp2 += 3;
+ }
+ break;
+ }
- return num;
+ case 4:
+ {
+ long *temp2 = (long *)temp;
+ double conv = 1.0 / 2147483648;
+ assert(sizeof(long) == 4);
+ for (int i = 0; i < numElems; i ++)
+ {
+ long value = temp2[i];
+ buffer[i] = (float)(_swap32(value) * conv);
+ }
+ break;
+ }
+ }
+
+ return numElems;
}
@@ -374,7 +485,7 @@ int WavInFile::readRIFFBlock()
if (fread(&(header.riff), sizeof(WavRiff), 1, fptr) != 1) return -1;
// swap 32bit data byte order if necessary
- _swap32((unsigned int &)header.riff.package_len);
+ _swap32((long &)header.riff.package_len);
// header.riff.riff_char should equal to 'RIFF');
if (memcmp(riffStr, header.riff.riff_char, 4) != 0) return -1;
@@ -401,7 +512,7 @@ int WavInFile::readHeaderBlock()
// Decode blocks according to their label
if (strcmp(label, fmtStr) == 0)
{
- int nLen, nDump;
+ long nLen, nDump;
// 'fmt ' block
memcpy(header.format.fmt, fmtStr, 4);
@@ -409,7 +520,7 @@ int WavInFile::readHeaderBlock()
// read length of the format field
if (fread(&nLen, sizeof(int), 1, fptr) != 1) return -1;
// swap byte order if necessary
- _swap32((unsigned int &)nLen); // int format_len;
+ _swap32(nLen); // int format_len;
header.format.format_len = nLen;
// calculate how much length differs from expected
@@ -425,12 +536,12 @@ int WavInFile::readHeaderBlock()
if (fread(&(header.format.fixed), nLen, 1, fptr) != 1) return -1;
// swap byte order if necessary
- _swap16((unsigned short &)header.format.fixed); // short int fixed;
- _swap16((unsigned short &)header.format.channel_number); // short int channel_number;
- _swap32((unsigned int &)header.format.sample_rate); // int sample_rate;
- _swap32((unsigned int &)header.format.byte_rate); // int byte_rate;
- _swap16((unsigned short &)header.format.byte_per_sample); // short int byte_per_sample;
- _swap16((unsigned short &)header.format.bits_per_sample); // short int bits_per_sample;
+ _swap16(header.format.fixed); // short int fixed;
+ _swap16(header.format.channel_number); // short int channel_number;
+ _swap32((long &)header.format.sample_rate); // int sample_rate;
+ _swap32((long &)header.format.byte_rate); // int byte_rate;
+ _swap16(header.format.byte_per_sample); // short int byte_per_sample;
+ _swap16(header.format.bits_per_sample); // short int bits_per_sample;
// if format_len is larger than expected, skip the extra data
if (nDump > 0)
@@ -447,7 +558,7 @@ int WavInFile::readHeaderBlock()
if (fread(&(header.data.data_len), sizeof(uint), 1, fptr) != 1) return -1;
// swap byte order if necessary
- _swap32((unsigned int &)header.data.data_len);
+ _swap32((long &)header.data.data_len);
return 1;
}
@@ -641,15 +752,15 @@ void WavOutFile::writeHeader()
// swap byte order if necessary
hdrTemp = header;
- _swap32((unsigned int &)hdrTemp.riff.package_len);
- _swap32((unsigned int &)hdrTemp.format.format_len);
- _swap16((unsigned short &)hdrTemp.format.fixed);
- _swap16((unsigned short &)hdrTemp.format.channel_number);
- _swap32((unsigned int &)hdrTemp.format.sample_rate);
- _swap32((unsigned int &)hdrTemp.format.byte_rate);
- _swap16((unsigned short &)hdrTemp.format.byte_per_sample);
- _swap16((unsigned short &)hdrTemp.format.bits_per_sample);
- _swap32((unsigned int &)hdrTemp.data.data_len);
+ _swap32((long &)hdrTemp.riff.package_len);
+ _swap32((long &)hdrTemp.format.format_len);
+ _swap16((short &)hdrTemp.format.fixed);
+ _swap16((short &)hdrTemp.format.channel_number);
+ _swap32((long &)hdrTemp.format.sample_rate);
+ _swap32((long &)hdrTemp.format.byte_rate);
+ _swap16((short &)hdrTemp.format.byte_per_sample);
+ _swap16((short &)hdrTemp.format.bits_per_sample);
+ _swap32((long &)hdrTemp.data.data_len);
// write the supplemented header in the beginning of the file
fseek(fptr, 0, SEEK_SET);
@@ -685,6 +796,7 @@ void WavOutFile::write(const char *buffer, int numElems)
}
+
void WavOutFile::write(const short *buffer, int numElems)
{
int res;
@@ -708,7 +820,7 @@ void WavOutFile::write(const short *buffer, int numElems)
else
{
// 16bit format
- unsigned short *pTemp = new unsigned short[numElems];
+ short *pTemp = new short[numElems];
if (header.format.bits_per_sample != 16)
{
@@ -736,25 +848,87 @@ void WavOutFile::write(const short *buffer, int numElems)
}
+/// Convert from float to integer and saturate
+inline long saturate(float fvalue, float minval, float maxval)
+{
+ if (fvalue > maxval)
+ {
+ fvalue = maxval;
+ }
+ else if (fvalue < minval)
+ {
+ fvalue = minval;
+ }
+ return (long)fvalue;
+}
+
+
void WavOutFile::write(const float *buffer, int numElems)
{
- int i;
- short *temp = new short[numElems];
- int iTemp;
+ int numBytes;
+ int bytesPerSample;
+
+ if (numElems == 0) return;
+
+ bytesPerSample = header.format.bits_per_sample / 8;
+ numBytes = numElems * bytesPerSample;
+ short *temp = (short*)getConvBuffer(numBytes);
- // convert to 16 bit integer
- for (i = 0; i < numElems; i ++)
+ switch (bytesPerSample)
{
- // convert to integer
- iTemp = (int)(32768.0f * buffer[i]);
+ case 1:
+ {
+ char *temp2 = (char *)temp;
+ for (int i = 0; i < numElems; i ++)
+ {
+ temp2[i] = (char)saturate(buffer[i] * 128.0f, -128.0f, 127.0f);
+ }
+ break;
+ }
+
+ case 2:
+ {
+ short *temp2 = (short *)temp;
+ for (int i = 0; i < numElems; i ++)
+ {
+ short value = (short)saturate(buffer[i] * 32768.0f, -32768.0f, 32767.0f);
+ temp2[i] = _swap16(value);
+ }
+ break;
+ }
- // saturate
- if (iTemp < -32768) iTemp = -32768;
- if (iTemp > 32767) iTemp = 32767;
- temp[i] = (short)iTemp;
+ case 3:
+ {
+ char *temp2 = (char *)temp;
+ for (int i = 0; i < numElems; i ++)
+ {
+ long value = saturate(buffer[i] * 8388608.0f, -8388608.0f, 8388607.0f);
+ *((long*)temp2) = _swap32(value);
+ temp2 += 3;
+ }
+ break;
+ }
+
+ case 4:
+ {
+ long *temp2 = (long *)temp;
+ for (int i = 0; i < numElems; i ++)
+ {
+ long value = saturate(buffer[i] * 2147483648.0f, -2147483648.0f, 2147483647.0f);
+ temp2[i] = _swap32(value);
+ }
+ break;
+ }
+
+ default:
+ assert(false);
}
- write(temp, numElems);
+ int res = fwrite(temp, 1, numBytes, fptr);
- delete[] temp;
+ if (res != numBytes)
+ {
+ ST_THROW_RT_ERROR("Error while writing to a wav file.");
+ }
+ bytesWritten += numBytes;
}
diff --git a/source/SoundStretch/WavFile.h b/source/SoundStretch/WavFile.h
index bf9b3bd..5b2cd59 100644
--- a/source/SoundStretch/WavFile.h
+++ b/source/SoundStretch/WavFile.h
@@ -92,8 +92,25 @@ typedef struct
} WavHeader;
+/// Base class for processing WAV audio files.
+class WavFileBase
+{
+private:
+ /// Conversion working buffer;
+ void *convBuff;
+ int convBuffSize;
+
+protected:
+ WavFileBase();
+ virtual ~WavFileBase();
+
+ /// Get pointer to conversion buffer of at min. given size
+ void *getConvBuffer(int sizeByte);
+};
+
+
/// Class for reading WAV audio files.
-class WavInFile
+class WavInFile : protected WavFileBase
{
private:
/// File pointer.
@@ -177,6 +194,7 @@ public:
/// Reads audio samples from the WAV file to floating point format, converting
/// sample values to range [-1,1[. Reads given number of elements from the file
/// or if end-of-file reached, as many elements as are left in the file.
+ /// Notice that reading in float format supports 8/16/24/32bit sample formats.
///
/// \return Number of elements read from the file.
int read(float *buffer, ///< Pointer to buffer where to read data.
@@ -192,7 +210,7 @@ public:
/// Class for writing WAV audio files.
-class WavOutFile
+class WavOutFile : protected WavFileBase
{
private:
/// Pointer to the WAV file