bullet3/examples/TinyAudio/b3ReadWavFile.cpp

493 lines
12 KiB
C++
Raw Normal View History

//b3ReadWavFile is implemented based on code from the STK toolkit
//See https://github.com/thestk/stk
//Some improvement: the ticking data (b3WavTicker) is separate from wav file,
//This makes it possoble to play a single wav multiple times at the same time
#include "b3ReadWavFile.h"
#include "b3SwapUtils.h"
const unsigned long B3_SINT8 = 0x1;
const unsigned long B3_SINT16 = 0x2;
const unsigned long B3_SINT24 = 0x4;
const unsigned long B3_SINT32 = 0x8;
const unsigned long B3_FLOAT32 = 0x10;
const unsigned long B3_FLOAT64 = 0x20;
b3ReadWavFile::b3ReadWavFile()
{
2017-04-27 19:07:31 +00:00
fd_ = 0;
m_machineIsLittleEndian = b3MachineIsLittleEndian();
}
b3ReadWavFile::~b3ReadWavFile()
{
if (fd_)
fclose(fd_);
}
void b3ReadWavFile::normalize(double peak)
{
int i;
double max = 0.0;
for (i = 0; i < m_frames.size(); i++)
{
if (fabs(m_frames[i]) > max)
max = (double)fabs((double)m_frames[i]);
}
if (max > 0.0)
{
max = 1.0 / max;
max *= peak;
for (i = 0; i < m_frames.size(); i++)
m_frames[i] *= max;
}
}
double b3ReadWavFile::interpolate(double frame, unsigned int channel) const
{
int iIndex = (int)frame; // integer part of index
double output, alpha = frame - (double)iIndex; // fractional part of index
iIndex = iIndex * channels_ + channel;
output = m_frames[iIndex];
if (alpha > 0.0)
output += (alpha * (m_frames[iIndex + channels_] - output));
return output;
}
double b3ReadWavFile::tick(unsigned int channel, b3WavTicker *ticker)
{
if (ticker->finished_) return 0.0;
if (ticker->time_ < 0.0 || ticker->time_ > (double)(this->m_numFrames - 1.0))
{
for (int i = 0; i < ticker->lastFrame_.size(); i++) ticker->lastFrame_[i] = 0.0;
ticker->finished_ = true;
return 0.0;
}
double tyme = ticker->time_;
bool interpolate_ = true; //for now
if (interpolate_)
{
for (int i = 0; i < ticker->lastFrame_.size(); i++)
ticker->lastFrame_[i] = interpolate(tyme, i);
}
// Increment time, which can be negative.
ticker->time_ += ticker->rate_;
return ticker->lastFrame_[channel];
}
void b3ReadWavFile::resize()
{
m_frames.resize(channels_ * m_numFrames);
}
b3WavTicker b3ReadWavFile::createWavTicker(double sampleRate)
{
b3WavTicker ticker;
ticker.lastFrame_.resize(this->channels_);
ticker.time_ = 0;
ticker.finished_ = false;
ticker.rate_ = fileDataRate_ / sampleRate;
return ticker;
}
bool b3ReadWavFile::getWavInfo(const char *fileName)
{
fd_ = fopen(fileName, "rb");
if (fd_ == 0)
return false;
char header[12];
if (fread(&header, 4, 3, fd_) != 3)
return false;
bool res = false;
if (!strncmp(header, "RIFF", 4) &&
!strncmp(&header[8], "WAVE", 4))
res = true;
//getWavInfo( fileName );
// Find "format" chunk ... it must come before the "data" chunk.
char id[4];
int chunkSize;
if (fread(&id, 4, 1, fd_) != 1)
return false;
while (strncmp(id, "fmt ", 4))
{
if (fread(&chunkSize, 4, 1, fd_) != 1)
return false;
if (!m_machineIsLittleEndian)
{
b3Swap32((unsigned char *)&chunkSize);
}
if (fseek(fd_, chunkSize, SEEK_CUR) == -1)
return false;
if (fread(&id, 4, 1, fd_) != 1)
return false;
}
// Check that the data is not compressed.
unsigned short format_tag;
if (fread(&chunkSize, 4, 1, fd_) != 1)
return false; // Read fmt chunk size.
if (fread(&format_tag, 2, 1, fd_) != 1)
return false;
if (!m_machineIsLittleEndian)
{
b3Swap16((unsigned char *)&format_tag);
b3Swap32((unsigned char *)&chunkSize);
}
if (format_tag == 0xFFFE)
{ // WAVE_FORMAT_EXTENSIBLE
dataOffset_ = ftell(fd_);
if (fseek(fd_, 14, SEEK_CUR) == -1)
return false;
unsigned short extSize;
if (fread(&extSize, 2, 1, fd_) != 1)
return false;
if (!m_machineIsLittleEndian)
{
b3Swap16((unsigned char *)&extSize);
}
if (extSize == 0)
return false;
if (fseek(fd_, 6, SEEK_CUR) == -1)
return false;
if (fread(&format_tag, 2, 1, fd_) != 1)
return false;
if (!m_machineIsLittleEndian)
{
b3Swap16((unsigned char *)&format_tag);
}
if (fseek(fd_, dataOffset_, SEEK_SET) == -1)
return false;
}
if (format_tag != 1 && format_tag != 3)
{ // PCM = 1, FLOAT = 3
// oStream_ << "FileRead: "<< fileName << " contains an unsupported data format type (" << format_tag << ").";
return false;
}
// Get number of channels from the header.
short int temp;
if (fread(&temp, 2, 1, fd_) != 1)
return false;
if (!m_machineIsLittleEndian)
{
b3Swap16((unsigned char *)&temp);
}
channels_ = (unsigned int)temp;
// Get file sample rate from the header.
int srate;
if (fread(&srate, 4, 1, fd_) != 1)
return false;
if (!m_machineIsLittleEndian)
{
b3Swap32((unsigned char *)&srate);
}
fileDataRate_ = (double)srate;
// Determine the data type.
dataType_ = 0;
if (fseek(fd_, 6, SEEK_CUR) == -1)
return false; // Locate bits_per_sample info.
if (fread(&temp, 2, 1, fd_) != 1)
return false;
if (!m_machineIsLittleEndian)
{
b3Swap16((unsigned char *)&temp);
}
if (format_tag == 1)
{
if (temp == 8)
dataType_ = B3_SINT8;
else if (temp == 16)
dataType_ = B3_SINT16;
else if (temp == 24)
dataType_ = B3_SINT24;
else if (temp == 32)
dataType_ = B3_SINT32;
}
else if (format_tag == 3)
{
if (temp == 32)
dataType_ = B3_FLOAT32;
else if (temp == 64)
dataType_ = B3_FLOAT64;
}
if (dataType_ == 0)
{
// oStream_ << "FileRead: " << temp << " bits per sample with data format " << format_tag << " are not supported (" << fileName << ").";
return false;
}
// Jump over any remaining part of the "fmt" chunk.
if (fseek(fd_, chunkSize - 16, SEEK_CUR) == -1)
return false;
// Find "data" chunk ... it must come after the "fmt" chunk.
if (fread(&id, 4, 1, fd_) != 1)
return false;
while (strncmp(id, "data", 4))
{
if (fread(&chunkSize, 4, 1, fd_) != 1)
return false;
if (!m_machineIsLittleEndian)
{
b3Swap32((unsigned char *)&chunkSize);
}
chunkSize += chunkSize % 2; // chunk sizes must be even
if (fseek(fd_, chunkSize, SEEK_CUR) == -1)
return false;
if (fread(&id, 4, 1, fd_) != 1)
return false;
}
// Get length of data from the header.
int bytes;
if (fread(&bytes, 4, 1, fd_) != 1)
return false;
if (!m_machineIsLittleEndian)
{
b3Swap32((unsigned char *)&bytes);
}
m_numFrames = bytes / temp / channels_; // sample frames
m_numFrames *= 8; // sample frames
dataOffset_ = ftell(fd_);
byteswap_ = false;
if (!m_machineIsLittleEndian)
{
byteswap_ = true;
}
wavFile_ = true;
return true;
}
bool b3ReadWavFile::read(unsigned long startFrame, bool doNormalize)
{
// Make sure we have an open file.
if (fd_ == 0)
{
// oStream_ << "FileRead::read: a file is not open!";
// Stk::handleError( StkError::WARNING ); return;
return false;
}
// Check the m_frames size.
unsigned long nFrames = this->m_numFrames; //m_frames.frames();
if (nFrames == 0)
{
// oStream_ << "FileRead::read: StkFrames m_frames size is zero ... no data read!";
// Stk::handleError( StkError::WARNING );
return false;
}
if (startFrame >= m_numFrames)
{
return false;
//oStream_ << "FileRead::read: startFrame argument is greater than or equal to the file size!";
//Stk::handleError( StkError::FUNCTION_ARGUMENT );
}
// Check for file end.
if (startFrame + nFrames > m_numFrames)
nFrames = m_numFrames - startFrame;
long i, nSamples = (long)(nFrames * channels_);
unsigned long offset = startFrame * channels_;
// Read samples into StkFrames data m_frames.
if (dataType_ == B3_SINT16)
{
signed short int *buf = (signed short int *)&m_frames[0];
if (fseek(fd_, dataOffset_ + (offset * 2), SEEK_SET) == -1)
return false;
if (fread(buf, nSamples * 2, 1, fd_) != 1)
return false;
if (byteswap_)
{
signed short int *ptr = buf;
for (i = nSamples - 1; i >= 0; i--)
b3Swap16((unsigned char *)ptr++);
}
if (doNormalize)
{
double gain = 1.0 / 32768.0;
for (i = nSamples - 1; i >= 0; i--)
m_frames[i] = buf[i] * gain;
}
else
{
for (i = nSamples - 1; i >= 0; i--)
m_frames[i] = buf[i];
}
}
else if (dataType_ == B3_SINT32)
{
int *buf = (int *)&m_frames[0];
if (fseek(fd_, dataOffset_ + (offset * 4), SEEK_SET) == -1)
return false;
if (fread(buf, nSamples * 4, 1, fd_) != 1)
return false;
if (byteswap_)
{
int *ptr = buf;
for (i = nSamples - 1; i >= 0; i--)
b3Swap32((unsigned char *)ptr++);
}
if (doNormalize)
{
double gain = 1.0 / 2147483648.0;
for (i = nSamples - 1; i >= 0; i--)
m_frames[i] = buf[i] * gain;
}
else
{
for (i = nSamples - 1; i >= 0; i--)
m_frames[i] = buf[i];
}
}
else if (dataType_ == B3_FLOAT32)
{
float *buf = (float *)&m_frames[0];
if (fseek(fd_, dataOffset_ + (offset * 4), SEEK_SET) == -1)
return false;
if (fread(buf, nSamples * 4, 1, fd_) != 1)
return false;
if (byteswap_)
{
float *ptr = buf;
for (i = nSamples - 1; i >= 0; i--)
b3Swap32((unsigned char *)ptr++);
}
for (i = nSamples - 1; i >= 0; i--)
m_frames[i] = buf[i];
}
else if (dataType_ == B3_FLOAT64)
{
double *buf = (double *)&m_frames[0];
if (fseek(fd_, dataOffset_ + (offset * 8), SEEK_SET) == -1)
return false;
if (fread(buf, nSamples * 8, 1, fd_) != 1)
return false;
if (byteswap_)
{
double *ptr = buf;
for (i = nSamples - 1; i >= 0; i--)
b3Swap64((unsigned char *)ptr++);
}
for (i = nSamples - 1; i >= 0; i--)
m_frames[i] = buf[i];
}
else if (dataType_ == B3_SINT8 && wavFile_)
{ // 8-bit WAV data is unsigned!
unsigned char *buf = (unsigned char *)&m_frames[0];
if (fseek(fd_, dataOffset_ + offset, SEEK_SET) == -1)
return false;
if (fread(buf, nSamples, 1, fd_) != 1)
return false;
if (doNormalize)
{
double gain = 1.0 / 128.0;
for (i = nSamples - 1; i >= 0; i--)
m_frames[i] = (buf[i] - 128) * gain;
}
else
{
for (i = nSamples - 1; i >= 0; i--)
m_frames[i] = buf[i] - 128.0;
}
}
else if (dataType_ == B3_SINT8)
{ // signed 8-bit data
char *buf = (char *)&m_frames[0];
if (fseek(fd_, dataOffset_ + offset, SEEK_SET) == -1)
return false;
if (fread(buf, nSamples, 1, fd_) != 1)
return false;
if (doNormalize)
{
double gain = 1.0 / 128.0;
for (i = nSamples - 1; i >= 0; i--)
m_frames[i] = buf[i] * gain;
}
else
{
for (i = nSamples - 1; i >= 0; i--)
m_frames[i] = buf[i];
}
}
else if (dataType_ == B3_SINT24)
{
// 24-bit values are harder to import efficiently since there is
// no native 24-bit type. The following routine works but is much
// less efficient than that used for the other data types.
int temp;
unsigned char *ptr = (unsigned char *)&temp;
double gain = 1.0 / 2147483648.0;
if (fseek(fd_, dataOffset_ + (offset * 3), SEEK_SET) == -1)
return false;
for (i = 0; i < nSamples; i++)
{
if (m_machineIsLittleEndian)
{
if (byteswap_)
{
if (fread(ptr, 3, 1, fd_) != 1)
return false;
temp &= 0x00ffffff;
b3Swap32((unsigned char *)ptr);
}
else
{
if (fread(ptr + 1, 3, 1, fd_) != 1)
return false;
temp &= 0xffffff00;
}
}
else
{
if (byteswap_)
{
if (fread(ptr + 1, 3, 1, fd_) != 1)
return false;
temp &= 0xffffff00;
b3Swap32((unsigned char *)ptr);
}
else
{
if (fread(ptr, 3, 1, fd_) != 1)
return false;
temp &= 0x00ffffff;
}
}
if (doNormalize)
{
m_frames[i] = (double)temp * gain; // "gain" also includes 1 / 256 factor.
}
else
m_frames[i] = (double)temp / 256; // right shift without affecting the sign bit
}
}
// m_frames.setDataRate( fileDataRate_ );
return true;
// error:
// oStream_ << "FileRead: Error reading file data.";
// handleError( StkError::FILE_ERROR);
2017-04-27 19:07:31 +00:00
}