- allow WavFileReader to read audio without resampling

This commit is contained in:
Dmytro Bogovych 2023-05-31 22:55:36 +03:00
parent 21fb865d80
commit 2aba79353a
2 changed files with 263 additions and 247 deletions

View File

@ -19,13 +19,13 @@
#endif
typedef struct {
WORD wFormatTag;
WORD nChannels;
DWORD nSamplesPerSec;
DWORD nAvgBytesPerSec;
WORD nBlockAlign;
WORD wBitsPerSample;
WORD cbSize;
WORD wFormatTag;
WORD nChannels;
DWORD nSamplesPerSec;
DWORD nAvgBytesPerSec;
WORD nBlockAlign;
WORD wBitsPerSample;
WORD cbSize;
}
WaveFormatEx;
@ -39,9 +39,9 @@ using namespace Audio;
// ---------------------- WavFileReader -------------------------
WavFileReader::WavFileReader()
:mHandle(nullptr), mRate(0), mLastError(0)
:mHandle(nullptr), mSamplerate(0), mLastError(0)
{
mDataOffset = 0;
mDataOffset = 0;
}
WavFileReader::~WavFileReader()
@ -52,207 +52,221 @@ WavFileReader::~WavFileReader()
std::string WavFileReader::readChunk()
{
char name[5];
if (fread(name, 1, 4, mHandle) != 4)
THROW_READERROR;
char name[5];
if (fread(name, 1, 4, mHandle) != 4)
THROW_READERROR;
name[4] = 0;
std::string result = name;
unsigned size;
if (fread(&size, 4, 1, mHandle) != 1)
THROW_READERROR;
name[4] = 0;
std::string result = name;
unsigned size;
if (fread(&size, 4, 1, mHandle) != 1)
THROW_READERROR;
if (result == "fact")
fread(&mDataLength, 4, 1, mHandle);
else
if (result != "data")
fseek(mHandle, size, SEEK_CUR);
else
mDataLength = size;
return result;
if (result == "fact")
fread(&mDataLength, 4, 1, mHandle);
else
if (result != "data")
fseek(mHandle, size, SEEK_CUR);
else
mDataLength = size;
return result;
}
bool WavFileReader::open(const std::tstring& filename)
{
LOCK;
try
{
#ifdef WIN32
mHandle = _wfopen(filename.c_str(), L"rb");
#else
mHandle = fopen(strx::makeUtf8(filename).c_str(), "rb");
#endif
if (NULL == mHandle)
LOCK;
try
{
#ifdef WIN32
mHandle = _wfopen(filename.c_str(), L"rb");
#else
mHandle = fopen(strx::makeUtf8(filename).c_str(), "rb");
#endif
if (NULL == mHandle)
{
#if defined(TARGET_ANDROID) || defined(TARGET_LINUX) || defined(TARGET_OSX)
mLastError = errno;
mLastError = errno;
#endif
#if defined(TARGET_WIN)
mLastError = GetLastError();
mLastError = GetLastError();
#endif
return false;
return false;
}
mLastError = 0;
// Read the .WAV header
char riff[4];
if (fread(riff, 4, 1, mHandle) < 1)
THROW_READERROR;
if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F'))
THROW_READERROR;
// Read the file size
unsigned int filesize = 0;
if (fread(&filesize, 4, 1, mHandle) < 1)
THROW_READERROR;
char wavefmt[9];
if (fread(wavefmt, 8, 1, mHandle) < 1)
THROW_READERROR;
wavefmt[8] = 0;
if (strcmp(wavefmt, "WAVEfmt ") != 0)
THROW_READERROR;
unsigned fmtSize = 0;
if (fread(&fmtSize, 4, 1, mHandle) < 1)
THROW_READERROR;
unsigned fmtStart = ftell(mHandle);
unsigned short formattag = 0;
if (fread(&formattag, 2, 1, mHandle) < 1)
THROW_READERROR;
if (formattag != 1/*WAVE_FORMAT_PCM*/)
THROW_READERROR;
mChannels = 0;
if (fread(&mChannels, 2, 1, mHandle) < 1)
THROW_READERROR;
mSamplerate = 0;
if (fread(&mSamplerate, 4, 1, mHandle) < 1)
THROW_READERROR;
unsigned int avgbytespersec = 0;
if (fread(&avgbytespersec, 4, 1, mHandle) < 1)
THROW_READERROR;
unsigned short blockalign = 0;
if (fread(&blockalign, 2, 1, mHandle) < 1)
THROW_READERROR;
mBits = 0;
if (fread(&mBits, 2, 1, mHandle) < 1)
THROW_READERROR;
if (mBits !=8 && mBits != 16)
THROW_READERROR;
// Read the "chunk"
fseek(mHandle, fmtStart + fmtSize, SEEK_SET);
//unsigned pos = ftell(mHandle);
mDataLength = 0;
while (readChunk() != "data")
;
mFileName = filename;
mDataOffset = ftell(mHandle);
mResampler.start(AUDIO_CHANNELS, mSamplerate, AUDIO_SAMPLERATE);
}
mLastError = 0;
// Read the .WAV header
char riff[4];
if (fread(riff, 4, 1, mHandle) < 1)
THROW_READERROR;
if (!(riff[0] == 'R' && riff[1] == 'I' && riff[2] == 'F' && riff[3] == 'F'))
THROW_READERROR;
// Read the file size
unsigned int filesize = 0;
if (fread(&filesize, 4, 1, mHandle) < 1)
THROW_READERROR;
char wavefmt[9];
if (fread(wavefmt, 8, 1, mHandle) < 1)
THROW_READERROR;
wavefmt[8] = 0;
if (strcmp(wavefmt, "WAVEfmt ") != 0)
THROW_READERROR;
unsigned fmtSize = 0;
if (fread(&fmtSize, 4, 1, mHandle) < 1)
THROW_READERROR;
unsigned fmtStart = ftell(mHandle);
unsigned short formattag = 0;
if (fread(&formattag, 2, 1, mHandle) < 1)
THROW_READERROR;
if (formattag != 1/*WAVE_FORMAT_PCM*/)
THROW_READERROR;
mChannels = 0;
if (fread(&mChannels, 2, 1, mHandle) < 1)
THROW_READERROR;
mRate = 0;
if (fread(&mRate, 4, 1, mHandle) < 1)
THROW_READERROR;
unsigned int avgbytespersec = 0;
if (fread(&avgbytespersec, 4, 1, mHandle) < 1)
THROW_READERROR;
unsigned short blockalign = 0;
if (fread(&blockalign, 2, 1, mHandle) < 1)
THROW_READERROR;
mBits = 0;
if (fread(&mBits, 2, 1, mHandle) < 1)
THROW_READERROR;
if (mBits !=8 && mBits != 16)
THROW_READERROR;
// Read the "chunk"
fseek(mHandle, fmtStart + fmtSize, SEEK_SET);
//unsigned pos = ftell(mHandle);
mDataLength = 0;
while (readChunk() != "data")
;
mFileName = filename;
mDataOffset = ftell(mHandle);
mResampler.start(AUDIO_CHANNELS, mRate, AUDIO_SAMPLERATE);
}
catch(...)
{
fclose(mHandle); mHandle = nullptr;
mLastError = static_cast<unsigned>(-1);
}
return isOpened();
catch(...)
{
fclose(mHandle); mHandle = nullptr;
mLastError = static_cast<unsigned>(-1);
}
return isOpened();
}
void WavFileReader::close()
{
LOCK;
LOCK;
if (nullptr != mHandle)
fclose(mHandle);
mHandle = nullptr;
if (nullptr != mHandle)
fclose(mHandle);
mHandle = nullptr;
}
int WavFileReader::rate() const
int WavFileReader::samplerate() const
{
return mRate;
return mSamplerate;
}
unsigned WavFileReader::read(void* buffer, unsigned bytes)
int WavFileReader::channels() const
{
return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2)) * AUDIO_CHANNELS * 2;
return mChannels;
}
unsigned WavFileReader::read(short* buffer, unsigned samples)
unsigned WavFileReader::read(void* buffer, unsigned bytes, bool resample)
{
LOCK;
if (resample)
return read((short*)buffer, bytes / (AUDIO_CHANNELS * 2), true) * AUDIO_CHANNELS * 2;
else
return read((short*)buffer, bytes / channels() / sizeof(short), false) * channels() * sizeof(short);
}
if (!mHandle)
return 0;
unsigned WavFileReader::read(short* buffer, unsigned samples, bool resample)
{
LOCK;
// Get number of samples that must be read from source file
int requiredBytes = mResampler.getSourceLength(samples) * mChannels * mBits / 8;
void* temp = alloca(requiredBytes);
memset(temp, 0, requiredBytes);
if (!mHandle)
return 0;
// Find required size of input buffer
if (mDataLength)
{
unsigned filePosition = ftell(mHandle);
// Check how much data we can read
unsigned fileAvailable = mDataLength + mDataOffset - filePosition;
requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes;
}
// Get number of samples that must be read from source file
int requiredBytes = resample ? mResampler.getSourceLength(samples) * mChannels * mBits / 8 : samples * mChannels * sizeof(short);
void* temp = alloca(requiredBytes);
memset(temp, 0, requiredBytes);
/*int readSamples = */fread(temp, 1, requiredBytes, mHandle);// / mChannels / (mBits / 8);
size_t processedBytes = 0;
size_t result = mResampler.processBuffer(temp, requiredBytes, processedBytes,
buffer, samples * 2 * AUDIO_CHANNELS);
// Find required size of input buffer
if (mDataLength)
{
unsigned filePosition = ftell(mHandle);
return result / 2 / AUDIO_CHANNELS;
// Check how much data we can read
unsigned fileAvailable = mDataLength + mDataOffset - filePosition;
requiredBytes = (int)fileAvailable < requiredBytes ? (int)fileAvailable : requiredBytes;
}
/*int readSamples = */fread(temp, 1, requiredBytes, mHandle);// / mChannels / (mBits / 8);
if (resample)
{
size_t processedBytes = 0;
size_t result = mResampler.processBuffer(temp, requiredBytes, processedBytes,
buffer, samples * 2 * AUDIO_CHANNELS);
return result / 2 / AUDIO_CHANNELS;
}
else
return requiredBytes / channels() / sizeof(short);
}
bool WavFileReader::isOpened()
{
LOCK;
LOCK;
return (mHandle != 0);
return (mHandle != 0);
}
void WavFileReader::rewind()
{
LOCK;
if (mHandle)
fseek(mHandle, mDataOffset, SEEK_SET);
LOCK;
if (mHandle)
fseek(mHandle, mDataOffset, SEEK_SET);
}
std::tstring WavFileReader::filename() const
{
LOCK;
LOCK;
return mFileName;
return mFileName;
}
unsigned WavFileReader::size() const
{
LOCK;
LOCK;
return mDataLength;
return mDataLength;
}
unsigned WavFileReader::lastError() const
{
return mLastError;
return mLastError;
}
// ------------------------- WavFileWriter -------------------------
#define LOG_SUBSYTEM "WavFileWriter"
@ -260,135 +274,135 @@ unsigned WavFileReader::lastError() const
#define BITS_PER_CHANNEL 16
WavFileWriter::WavFileWriter()
:mHandle(nullptr), mLengthOffset(0), mRate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
:mHandle(nullptr), mLengthOffset(0), mRate(AUDIO_SAMPLERATE), mChannels(1), mWritten(0)
{
}
WavFileWriter::~WavFileWriter()
{
close();
close();
}
void WavFileWriter::checkWriteResult(int result)
{
if (result < 1)
throw Exception(ERR_WAVFILE_FAILED, errno);
if (result < 1)
throw Exception(ERR_WAVFILE_FAILED, errno);
}
bool WavFileWriter::open(const std::tstring& filename, int rate, int channels)
{
LOCK;
LOCK;
close();
mRate = rate;
mChannels = channels;
close();
mRate = rate;
mChannels = channels;
#ifdef WIN32
mHandle = _wfopen(filename.c_str(), L"wb");
mHandle = _wfopen(filename.c_str(), L"wb");
#else
mHandle = fopen(strx::makeUtf8(filename).c_str(), "wb");
mHandle = fopen(strx::makeUtf8(filename).c_str(), "wb");
#endif
if (nullptr == mHandle)
{
ICELogError(<< "Failed to create .wav file: filename = " << strx::makeUtf8(filename) << " , error = " << errno);
return false;
}
if (nullptr == mHandle)
{
ICELogError(<< "Failed to create .wav file: filename = " << strx::makeUtf8(filename) << " , error = " << errno);
return false;
}
// Write the .WAV header
const char* riff = "RIFF";
checkWriteResult( fwrite(riff, 4, 1, mHandle) );
// Write the file size
unsigned int filesize = 0;
checkWriteResult( fwrite(&filesize, 4, 1, mHandle) );
// Write the .WAV header
const char* riff = "RIFF";
checkWriteResult( fwrite(riff, 4, 1, mHandle) );
const char* wavefmt = "WAVEfmt ";
checkWriteResult( fwrite(wavefmt, 8, 1, mHandle) );
// Write the file size
unsigned int filesize = 0;
checkWriteResult( fwrite(&filesize, 4, 1, mHandle) );
// Set the format description
DWORD dwFmtSize = 16; /*= 16L*/;
checkWriteResult( fwrite(&dwFmtSize, sizeof(dwFmtSize), 1, mHandle) );
const char* wavefmt = "WAVEfmt ";
checkWriteResult( fwrite(wavefmt, 8, 1, mHandle) );
WaveFormatEx format;
format.wFormatTag = WAVE_FORMAT_PCM;
checkWriteResult( fwrite(&format.wFormatTag, sizeof(format.wFormatTag), 1, mHandle) );
format.nChannels = mChannels;
checkWriteResult( fwrite(&format.nChannels, sizeof(format.nChannels), 1, mHandle) );
// Set the format description
DWORD dwFmtSize = 16; /*= 16L*/;
checkWriteResult( fwrite(&dwFmtSize, sizeof(dwFmtSize), 1, mHandle) );
format.nSamplesPerSec = mRate;
checkWriteResult( fwrite(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec), 1, mHandle) );
format.nAvgBytesPerSec = mRate * 2 * mChannels;
checkWriteResult( fwrite(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec), 1, mHandle) );
WaveFormatEx format;
format.wFormatTag = WAVE_FORMAT_PCM;
checkWriteResult( fwrite(&format.wFormatTag, sizeof(format.wFormatTag), 1, mHandle) );
format.nBlockAlign = 2 * mChannels;
checkWriteResult( fwrite(&format.nBlockAlign, sizeof(format.nBlockAlign), 1, mHandle) );
format.wBitsPerSample = BITS_PER_CHANNEL;
checkWriteResult( fwrite(&format.wBitsPerSample, sizeof(format.wBitsPerSample), 1, mHandle) );
const char* data = "data";
checkWriteResult( fwrite(data, 4, 1, mHandle));
format.nChannels = mChannels;
checkWriteResult( fwrite(&format.nChannels, sizeof(format.nChannels), 1, mHandle) );
mFileName = filename;
mWritten = 0;
format.nSamplesPerSec = mRate;
checkWriteResult( fwrite(&format.nSamplesPerSec, sizeof(format.nSamplesPerSec), 1, mHandle) );
mLengthOffset = ftell(mHandle);
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
format.nAvgBytesPerSec = mRate * 2 * mChannels;
checkWriteResult( fwrite(&format.nAvgBytesPerSec, sizeof(format.nAvgBytesPerSec), 1, mHandle) );
return isOpened();
format.nBlockAlign = 2 * mChannels;
checkWriteResult( fwrite(&format.nBlockAlign, sizeof(format.nBlockAlign), 1, mHandle) );
format.wBitsPerSample = BITS_PER_CHANNEL;
checkWriteResult( fwrite(&format.wBitsPerSample, sizeof(format.wBitsPerSample), 1, mHandle) );
const char* data = "data";
checkWriteResult( fwrite(data, 4, 1, mHandle));
mFileName = filename;
mWritten = 0;
mLengthOffset = ftell(mHandle);
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
return isOpened();
}
void WavFileWriter::close()
{
LOCK;
LOCK;
if (mHandle)
{
fclose(mHandle);
mHandle = nullptr;
}
if (mHandle)
{
fclose(mHandle);
mHandle = nullptr;
}
}
size_t WavFileWriter::write(const void* buffer, size_t bytes)
{
LOCK;
LOCK;
if (!mHandle)
return 0;
if (!mHandle)
return 0;
// Seek the end of file
fseek(mHandle, 0, SEEK_END);
mWritten += bytes;
// Seek the end of file
fseek(mHandle, 0, SEEK_END);
mWritten += bytes;
// Write the data
fwrite(buffer, bytes, 1, mHandle);
// Write the data
fwrite(buffer, bytes, 1, mHandle);
// Write file length
fseek(mHandle, 4, SEEK_SET);
int32_t fl = mWritten + 36;
fwrite(&fl, sizeof(fl), 1, mHandle);
// Write data length
fseek(mHandle, static_cast<long>(mLengthOffset), SEEK_SET);
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
// Write file length
fseek(mHandle, 4, SEEK_SET);
int32_t fl = mWritten + 36;
fwrite(&fl, sizeof(fl), 1, mHandle);
return bytes;
// Write data length
fseek(mHandle, static_cast<long>(mLengthOffset), SEEK_SET);
checkWriteResult( fwrite(&mWritten, 4, 1, mHandle) );
return bytes;
}
bool WavFileWriter::isOpened()
{
LOCK;
LOCK;
return (mHandle != nullptr);
return (mHandle != nullptr);
}
std::tstring WavFileWriter::filename()
{
LOCK;
LOCK;
return mFileName;
return mFileName;
}

View File

@ -20,16 +20,17 @@ namespace Audio
class WavFileReader
{
protected:
FILE* mHandle;
short mChannels;
short mBits;
int mRate;
std::tstring mFileName;
mutable std::recursive_mutex mFileMtx;
unsigned mDataOffset;
unsigned mDataLength;
Resampler mResampler;
unsigned mLastError;
FILE* mHandle;
short mChannels;
short mBits;
int mSamplerate;
std::tstring mFileName;
mutable std::recursive_mutex
mFileMtx;
unsigned mDataOffset;
unsigned mDataLength;
Resampler mResampler;
unsigned mLastError;
std::string readChunk();
public:
@ -40,13 +41,14 @@ namespace Audio
void close();
bool isOpened();
void rewind();
int rate() const;
int samplerate() const;
int channels() const;
// This method returns number of read bytes
unsigned read(void* buffer, unsigned bytes);
unsigned read(void* buffer, unsigned bytes, bool resample = true);
// This method returns number of read samples
unsigned read(short* buffer, unsigned samples);
unsigned read(short* buffer, unsigned samples, bool resample = true);
std::tstring filename() const;
unsigned size() const;