📄 wavfile.cpp
字号:
/****************************************************************************** * * Classes for easy reading & writing of WAV sound files. * * For big-endian CPU, define _BIG_ENDIAN_ during compile-time to correctly * parse the WAV files with such processors. * * Admittingly, more complete WAV reader routines may exist in public domain, * but the reason for 'yet another' one is that those generic WAV reader * libraries are exhaustingly large and cumbersome! Wanted to have something * simpler here, i.e. something that's not already larger than rest of the * SoundTouch/SoundStretch program... * * Author : Copyright (c) Olli Parviainen * Author e-mail : oparviai @ iki.fi * File created : 13-Jan-2002 * * Last changed : $Date: 2004/10/26 19:09:39 $ * File revision : $Revision: 1.2 $ * * $Id: WavFile.cpp,v 1.2 2004/10/26 19:09:39 vjohnson Exp $ * * License : * * SoundTouch sound processing library * Copyright (c) Olli Parviainen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *****************************************************************************/#include <stdio.h>#include <stdexcept>#include <string>#include <assert.h>#include <limits.h>#include "WavFile.h"using namespace std;const static char riffStr[] = "RIFF";const static char waveStr[] = "WAVE";const static char fmtStr[] = "fmt ";const static char dataStr[] = "data";////////////////////////////////////////////////////////////////////////////////// Helper functions for swapping byte order to correctly read/write WAV files // with big-endian CPU's: Define compile-time definition _BIG_ENDIAN_ to// turn-on the conversion if it appears necessary. //// For example, Intel x86 is little-endian and doesn't require conversion,// while PowerPC of Mac's and many other RISC cpu's are big-endian.#ifdef BYTE_ORDER // In gcc compiler detect the byte order automatically #if BYTE_ORDER == BIG_ENDIAN // big-endian platform. #define _BIG_ENDIAN_ #endif#endif #ifdef _BIG_ENDIAN_ // 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) { dwData = ((dwData >> 24) & 0x000000FF) | ((dwData >> 8) & 0x0000FF00) | ((dwData << 8) & 0x00FF0000) | ((dwData << 24) & 0xFF000000); } // helper-function to swap byte-order of 16bit integer static inline void _swap16(unsigned short &wData) { wData = ((wData >> 8) & 0x00FF) | ((wData << 8) & 0xFF00); } // helper-function to swap byte-order of buffer of 16bit integers static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumWords) { unsigned long i; for (i = 0; i < dwNumWords; i ++) { _swap16(pData[i]); } }#else // BIG_ENDIAN // little-endian CPU, WAV file is ok as such // dummy helper-function static inline void _swap32(unsigned int &dwData) { // do nothing } // dummy helper-function static inline void _swap16(unsigned short &wData) { // do nothing } // dummy helper-function static inline void _swap16Buffer(unsigned short *pData, unsigned int dwNumBytes) { // do nothing }#endif // BIG_ENDIAN////////////////////////////////////////////////////////////////////////////////// Class WavInFile//WavInFile::WavInFile(const char *fileName){ int hdrsOk; // Try to open the file for reading fptr = fopen(fileName, "rb"); if (fptr == NULL) { // didn't succeed string msg = "Error : Unable to open file \""; msg += fileName; msg += "\" for reading."; throw runtime_error(msg); } // Read the file headers hdrsOk = readWavHeaders(); if (hdrsOk != 0) { // Something didn't match in the wav file headers string msg = "File \""; msg += fileName; msg += "\" is corrupt or not a WAV file"; throw runtime_error(msg); } if (header.format.fixed != 1) { string msg = "File \""; msg += fileName; msg += "\" uses unsupported encoding."; throw runtime_error(msg); } dataRead = 0;}WavInFile::~WavInFile(){ close();}void WavInFile::rewind(){ int hdrsOk; fseek(fptr, 0, SEEK_SET); hdrsOk = readWavHeaders(); assert(hdrsOk == 0); dataRead = 0;}int WavInFile::checkCharTags(){ // header.format.fmt should equal to 'fmt ' if (memcmp(fmtStr, header.format.fmt, 4) != 0) return -1; // header.data.data_field should equal to 'data' if (memcmp(dataStr, header.data.data_field, 4) != 0) return -1; return 0;}int WavInFile::read(void *buffer, const int bufferSizeInBytes){ unsigned int afterDataRead; int num; num = bufferSizeInBytes; afterDataRead = dataRead + num; if (afterDataRead > header.data.data_len) { // Don't read more samples than are marked available in header num = header.data.data_len - dataRead; assert(num >= 0); } num = fread(buffer, 1, num, fptr); dataRead += num; if (header.format.bits_per_sample == 16) { // 16bit samples, swap byte order if necessary _swap16Buffer((unsigned short *)buffer, num / 2); } else { // (should always be) 8 bit samples assert(header.format.bits_per_sample == 8); } return num;}int WavInFile::eof() const{ // return true if all data has been read or file eof has reached return (dataRead == header.data.data_len || feof(fptr));}void WavInFile::close(){ fclose(fptr); fptr = NULL;}// test if character code is between a white space ' ' and little 'z'static int isAlpha(char c){ return (c >= ' ' && c <= 'z') ? 1 : 0;}// test if all characters are between a white space ' ' and little 'z'static int isAlphaStr(char *str){ int c; c = str[0]; while (c) { if (isAlpha(c) == 0) return 0; str ++; c = str[0]; } return 1;}int WavInFile::readRIFFBlock(){ fread(&(header.riff), sizeof(WavRiff), 1, fptr); // swap 32bit data byte order if necessary _swap32((unsigned int &)header.riff.package_len); // header.riff.riff_char should equal to 'RIFF'); if (memcmp(riffStr, header.riff.riff_char, 4) != 0) return -1; // header.riff.wave should equal to 'WAVE' if (memcmp(waveStr, header.riff.wave, 4) != 0) return -1; return 0;}int WavInFile::readHeaderBlock(){ char label[5]; string sLabel; // lead label string fread(label, 1, 4, fptr); label[4] = 0; if (isAlphaStr(label) == 0) return -1; // not a valid label // Decode blocks according to their label if (strcmp(label, fmtStr) == 0) { int nLen, nDump; // 'fmt ' block memcpy(header.format.fmt, fmtStr, 4); // read length of the format field fread(&nLen, sizeof(int), 1, fptr); // swap byte order if necessary _swap32((unsigned int &)nLen); // int format_len; header.format.format_len = nLen; // calculate how much length differs from expected nDump = nLen - (sizeof(header.format) - 8); // if format_len is larger than expected, read only as much data as we've space for if (nDump > 0) { nLen = sizeof(header.format) - 8; } // read data fread(&(header.format.fixed), nLen, 1, fptr); // 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; // if format_len is larger than expected, skip the extra data if (nDump > 0) { fseek(fptr, nDump, SEEK_CUR); } return 0; } else if (strcmp(label, dataStr) == 0) { // 'data' block memcpy(header.data.data_field, dataStr, 4); fread(&(header.data.data_len), sizeof(uint), 1, fptr); // swap byte order if necessary _swap32((unsigned int &)header.data.data_len); return 1; } else { uint len, i; uint temp; // unknown block // read length fread(&len, sizeof(len), 1, fptr); // scan through the block for (i = 0; i < len; i ++) { fread(&temp, 1, 1, fptr); if (feof(fptr)) return -1; // unexpected eof } } return 0;}int WavInFile::readWavHeaders(){ int res; memset(&header, 0, sizeof(header)); res = readRIFFBlock(); if (res) return 1; // read header blocks until data block is found do { // read header blocks res = readHeaderBlock(); if (res < 0) return 1; // error in file structure } while (res == 0); // check that all required tags are legal return checkCharTags();}uint WavInFile::getNumChannels() const{ return header.format.channel_number;}uint WavInFile::getNumBits() const{ return header.format.bits_per_sample;}uint WavInFile::getBytesPerSample() const{ return getNumChannels() * getNumBits() / 8;}uint WavInFile::getSampleRate() const{ return header.format.sample_rate;}uint WavInFile::getDataSizeInBytes() const{ return header.data.data_len;}uint WavInFile::getNumSamples() const{ return header.data.data_len / header.format.byte_per_sample;}uint WavInFile::getLengthMS() const{ uint numSamples; uint sampleRate; numSamples = getNumSamples(); sampleRate = getSampleRate(); assert(numSamples < UINT_MAX / 1000); return (1000 * numSamples / sampleRate);}////////////////////////////////////////////////////////////////////////////////// Class WavOutFile//WavOutFile::WavOutFile(const char *fileName, const int sampleRate, const int bits, const int channels){ bytesWritten = 0; fptr = fopen(fileName, "wb"); if (fptr == NULL) { string msg = "Error : Unable to open file \""; msg += fileName; msg += "\" for writing."; //pmsg = msg.c_str; throw runtime_error(msg); } fillInHeader(sampleRate, bits, channels); writeHeader();}WavOutFile::~WavOutFile(){ close();}void WavOutFile::fillInHeader(uint sampleRate, uint bits, uint channels){ // fill in the 'riff' part.. // copy string 'RIFF' to riff_char memcpy(&(header.riff.riff_char), riffStr, 4); // package_len unknown so far header.riff.package_len = 0; // copy string 'WAVE' to wave memcpy(&(header.riff.wave), waveStr, 4); // fill in the 'format' part.. // copy string 'fmt ' to fmt memcpy(&(header.format.fmt), fmtStr, 4); header.format.format_len = 0x10; header.format.fixed = 1; header.format.channel_number = (short)channels; header.format.sample_rate = sampleRate; header.format.bits_per_sample = (short)bits; header.format.byte_per_sample = (short)(bits * channels / 8); header.format.byte_rate = header.format.byte_per_sample * sampleRate; header.format.sample_rate = sampleRate; // fill in the 'data' part.. // copy string 'data' to data_field memcpy(&(header.data.data_field), dataStr, 4); // data_len unknown so far header.data.data_len = 0;}void WavOutFile::finishHeader(){ // supplement the file length into the header structure header.riff.package_len = bytesWritten + 36; header.data.data_len = bytesWritten; writeHeader();}void WavOutFile::writeHeader(){ WavHeader hdrTemp; // 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); // write the supplemented header in the beginning of the file fseek(fptr, 0, SEEK_SET); fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr); // jump back to the end of the file fseek(fptr, 0, SEEK_END);}void WavOutFile::close(){ finishHeader(); fclose(fptr); fptr = NULL;}void WavOutFile::write(const void *buffer, const int numBytes){ int res; if (header.format.bits_per_sample == 16) { unsigned short *pTemp; // 16 bit samples if (numBytes < 2) return; // nothing to do // allocate temp buffer to swap byte order if necessary pTemp = new unsigned short[numBytes / 2]; memcpy(pTemp, buffer, numBytes); _swap16Buffer(pTemp, numBytes / 2); res = fwrite(pTemp, 1, numBytes, fptr); delete[] pTemp; } else { // 8bit samples if (numBytes < 1) return; // nothing to do assert(header.format.bits_per_sample == 8); res = fwrite(buffer, 1, numBytes, fptr); } if (res != numBytes) { throw runtime_error("Error while writing to a wav file."); } bytesWritten += numBytes;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -