| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492 |
- //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()
- {
- 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);
- }
|