// // Programmer: Craig Stuart Sapp // Creation Date: Fri May 23 21:40:12 GMT-0800 1997 // Last Modified: Wed Jan 14 23:56:16 GMT-0800 1998 // Filename: ...sig/maint/code/src/base/SoundHeader/SoundHeader.cpp // Web Address: http://sig.sapp.org/src/sigBase/SoundHeader.cpp // Documentation: http://sig.sapp.org/doc/classes/SoundHeader // Syntax: C++ // #include "SoundHeader.h" #include #include #include #ifndef OLDCPP #include using namespace std; #else #include #endif ////////////////////////////// // // SoundHeader::SoundHeader -- default set to 44100 sampling rate, // 1 channel, 16-bit linear. // SoundHeader::SoundHeader(void) { inputType = TYPE_NONE; outputType = TYPE_DEFAULT; bitsPerSample = 16; channels = 1; commentLength = 0; commentString = new char[1]; commentString[0] = '\0'; NeXT_DataFormat = SND_FORMAT_LINEAR_16; samples = 0; samplingRate = 44100; } SoundHeader::SoundHeader(char* aFilename) { commentLength = 0; commentString = new char[1]; commentString[0] = '\0'; setHeader(aFilename); } SoundHeader::SoundHeader(SoundHeader& aHeader) { inputType = aHeader.inputType; outputType = aHeader.outputType; bitsPerSample = aHeader.bitsPerSample; channels = aHeader.channels; commentLength = aHeader.commentLength; commentString = new char[commentLength + 1]; strcpy(commentString, aHeader.commentString); NeXT_DataFormat = aHeader.NeXT_DataFormat; samples = aHeader.samples; samplingRate = aHeader.samplingRate; } ////////////////////////////// // // SoundHeader::~SoundHeader -- // SoundHeader::~SoundHeader() { if (commentString != NULL) { delete [] commentString; } } ////////////////////////////// // // SoundHeader::getBitsPerSample -- // int SoundHeader::getBitsPerSample(void) const { return bitsPerSample; } ////////////////////////////// // // SoundHeader::getBlockAlign -- returns the number of bytes // in a single sample set. // int SoundHeader::getBlockAlign(void) const { return blockAlign; } ////////////////////////////// // // SoundHeader::getBytesPerSample -- // int SoundHeader::getBytesPerSample(void) const { return sampleBytes; } ////////////////////////////// // // SoundHeader::getChannels -- // int SoundHeader::getChannels(void) const { return channels; } ////////////////////////////// // // SoundHeader::getComment -- // const char* SoundHeader::getComment(void) const { return commentString; } ////////////////////////////// // // SoundHeader::getCommentSize -- // int SoundHeader::getCommentSize(void) const { return commentLength; } ////////////////////////////// // // SoundHeader::getDataByteOffset -- returns the byte offset into the // soundfile of the data. Offset is from 0. Returns -1 if // there is an error such as an invalid format type. // default value: aType = TYPE_NONE // int SoundHeader::getDataByteOffset(int aType) const { int realType = aType == TYPE_NONE ? outputType : aType; switch (realType) { case TYPE_SND: if (getCommentSize() < 4) { return 28; } else if (getCommentSize() % 2 == 0) { return 24 + getCommentSize(); } else { // keep data start at even return 24 + getCommentSize() + 1; // offset for certain NeXTStep } // sound programs break; case TYPE_WAV_PCM: return 44; break; default: return -1; } } ////////////////////////////// // // SoundHeader::getDataByteSize -- returns the total number of // bytes in the sound data area. // int SoundHeader::getDataByteSize(void) const { return (int)(getBitsPerSample() / 8.0 * getSamples() * getChannels()); } ////////////////////////////// // // SoundHeader::getInputType -- // int SoundHeader::getInputType(void) const { return inputType; } ////////////////////////////// // // SoundHeader::getNextFormat -- // int SoundHeader::getNextFormat(void) const { return NeXT_DataFormat; } ////////////////////////////// // // SoundHeader::getOutputType -- the soundfile format that // will be output by the operator<<. // int SoundHeader::getOutputType(void) const { return outputType; } ////////////////////////////// // // SoundHeader::getSamples -- returns the number of samples // in the soundfile per channel. // int SoundHeader::getSamples(void) const { return samples; } ////////////////////////////// // // SoundHeader::getSrate -- returns the sampling rate // int SoundHeader::getSrate(void) const { return samplingRate; } ////////////////////////////// // // SoundHeader::modifyFormat -- used for writing out soundfile in // a different format than was read in. You do not want // to modify a header for a file you are reading in! // You modify a header to write a new file which has // a different format from the input. // void SoundHeader::modifyFormat(const char* filename) { int aFormat = getFormat(filename); switch (aFormat) { case FORMAT_SND_MULAW_8: NeXT_DataFormat = SND_FORMAT_MULAW_8; bitsPerSample = 8; outputType = TYPE_SND; break; case FORMAT_SND_LINEAR_8: NeXT_DataFormat = SND_FORMAT_LINEAR_8; bitsPerSample = 8; outputType = TYPE_SND; break; case FORMAT_SND_LINEAR_16: NeXT_DataFormat = SND_FORMAT_LINEAR_16; bitsPerSample = 16; outputType = TYPE_SND; break; case FORMAT_SND_LINEAR_24: NeXT_DataFormat = SND_FORMAT_LINEAR_24; bitsPerSample = 24; outputType = TYPE_SND; break; case FORMAT_SND_LINEAR_32: NeXT_DataFormat = SND_FORMAT_LINEAR_32; bitsPerSample = 32; outputType = TYPE_SND; break; case FORMAT_SND_FLOAT: NeXT_DataFormat = SND_FORMAT_FLOAT; bitsPerSample = 32; outputType = TYPE_SND; break; case FORMAT_SND_DOUBLE: NeXT_DataFormat = SND_FORMAT_DOUBLE; bitsPerSample = 64; outputType = TYPE_SND; break; case FORMAT_WAV_LINEAR_8: NeXT_DataFormat = SND_FORMAT_LINEAR_8; bitsPerSample = 8; outputType = TYPE_WAV; break; case FORMAT_WAV_LINEAR_16: NeXT_DataFormat = SND_FORMAT_LINEAR_16; bitsPerSample = 16; outputType = TYPE_WAV; break; case FORMAT_WAV_LINEAR_24: NeXT_DataFormat = SND_FORMAT_LINEAR_24; bitsPerSample = 24; outputType = TYPE_WAV; break; case FORMAT_WAV_LINEAR_32: NeXT_DataFormat = SND_FORMAT_LINEAR_32; bitsPerSample = 32; outputType = TYPE_WAV; break; default: cerr << "Error: unknow output filetype: " << filename << endl; exit(1); } updateDerivedVariables(); } ////////////////////////////// // // SoundHeader::operator= -- // SoundHeader& SoundHeader::operator=(const SoundHeader& h) { // don't do anything if assigning to itself: if (this == &h) return *this; bitsPerSample = h.bitsPerSample; channels = h.channels; commentLength = h.commentLength; NeXT_DataFormat = h.NeXT_DataFormat; samples = h.samples; samplingRate = h.samplingRate; inputType = h.inputType; outputType = h.outputType; if (commentString != NULL) { delete [] commentString; } commentString = new char[commentLength+1]; strcpy(commentString, h.commentString); blockAlign = h.blockAlign; sampleBytes = h.sampleBytes; return *this; } ////////////////////////////// // // SoundHeader::setChannels -- // void SoundHeader::setChannels(int numChannels) { channels = numChannels; updateDerivedVariables(); } ////////////////////////////// // // SoundHeader::setComment -- // void SoundHeader::setComment(const char* aString) { commentLength = strlen(aString); if (commentString != NULL) { delete [] commentString; } commentString = new char[commentLength+1]; strcpy(commentString, aString); } ////////////////////////////// // // SoundHeader::setHeader -- // void SoundHeader::setHeader(const char* filename) { int format = getFormat(filename); switch (format) { case FORMAT_SND_MULAW_8: case FORMAT_SND_LINEAR_8: case FORMAT_SND_LINEAR_16: case FORMAT_SND_LINEAR_24: case FORMAT_SND_LINEAR_32: case FORMAT_SND_FLOAT: case FORMAT_SND_DOUBLE: inputType = TYPE_SND; break; case FORMAT_WAV_LINEAR_8: case FORMAT_WAV_LINEAR_16: case FORMAT_WAV_LINEAR_24: case FORMAT_WAV_LINEAR_32: inputType = TYPE_WAV; break; default: cerr << "Unknown input soundfile type: " << filename << endl; exit(1); } switch (inputType) { case TYPE_SND: processInSndFormat(filename); break; case TYPE_WAV: processInWavFormat(filename); break; default: cerr << "Unknown soundfile format for file: " << filename << endl; exit(1); } } ////////////////////////////// // // SoundHeader::setHighMono() -- set the header to // 44100 sampling rate, 16-bit samples, 1 channel. // default value: comment = "" // void SoundHeader::setHighMono(const char* comment) { setSrate(44100); setChannels(1); setComment(comment); setNextFormat(SND_FORMAT_LINEAR_16); } ////////////////////////////// // // SoundHeader::setHighStereo() -- set the header to // 44100 sampling rate, 16-bit samples, 2 channels. // default value: comment = "" // void SoundHeader::setHighStereo(const char* comment) { setSrate(44100); setChannels(2); setComment(comment); setNextFormat(SND_FORMAT_LINEAR_16); } ////////////////////////////// // // SoundHeader::setNextFormat -- // default value: format = SND_FORMAT_LINEAR_16 // void SoundHeader::setNextFormat(int format) { // update internal variables based on the NeXT_DataFormat: switch (format) { case SND_FORMAT_MULAW_8: bitsPerSample = 8; break; case SND_FORMAT_LINEAR_8: bitsPerSample = 8; break; case SND_FORMAT_LINEAR_16: bitsPerSample = 16; break; case SND_FORMAT_LINEAR_24: bitsPerSample = 24; break; case SND_FORMAT_LINEAR_32: bitsPerSample = 32; break; case SND_FORMAT_FLOAT: bitsPerSample = 32; break; case SND_FORMAT_DOUBLE: bitsPerSample = 64; break; default: cerr << "Unknown format: " << format << endl; exit(1); } NeXT_DataFormat = format; } ////////////////////////////// // // SoundHeader::setOutputType -- returns old format. // default value: aType = TYPE_DEFAULT // int SoundHeader::setOutputType(int aType) { int temp = outputType; outputType = aType; return temp; } ////////////////////////////// // // SoundHeader::setSamples -- // void SoundHeader::setSamples(int numSamples) { samples = numSamples; } ////////////////////////////// // // SoundHeader::setSrate -- // void SoundHeader::setSrate(int aSrate) { samplingRate = aSrate; } ////////////////////////////// // // (SoundHeader::)operator<< -- // ostream& operator<<(ostream& output, SoundHeader& header) { output << "Bits per sample: " << header.getBitsPerSample() << endl; output << "Channels: " << header.getChannels() <= 0 && string[index] != '.') { sum += (int)tolower(string[index]); index--; } index++; strncpy(extension, &string[index], 1000); int length = strlen(extension); if (length > 200) length = 200; for (int i=0; i. Common formats are: * SND_FORMAT_MULAW_8 (1) * SND_FORMAT_LINEAR_16 (3) * SND_FORMAT_FLOAT (6) * 16 : 4 samplingRate The sampling rate. Hardware supported sampling rates * are: * SND_RATE_CODEC (8012.8210513) * SND_RATE_LOW (22050.0) * SND_RATE_HIGH (44100.0) * SND_RATE_LOW_PC (11025.0) * 20 : 4 channelCount The number of channels * 24 : 4+X info[4] Comments relating to the sound, or user * defined data fields. Must be at least 4 bytes long, * but can be as long as desired, since the data starts * at the offset in bytes from the beginning of the * soundfile according to the number in the dataLocation * field. * 28+X: * data the actual sound data bytes. * */ ////////////////////////////// // // SoundHeader::processInWavFormat // void SoundHeader::processInWavFormat(const char *filename) { FileIO sndfile; sndfile.open(filename, ios::in); if (!sndfile.is_open()) { cerr << "Error: sound file " << filename << " could not be opened!" << endl; exit(1); } ulong tempLong; ushort tempShort; // read the ChunkID, which should be the characters "RIFF": sndfile.readBigEndian(tempLong); if (tempLong != 0x52494646) { cerr << "Error: ChunkID expected to be 0x52494646 (RIFF), but is: " << hex << tempLong << endl; exit(1); } // read the ChunkSize (ignore for Canonical WAVE format): sndfile.readLittleEndian(tempLong); // read the Format: sndfile.readBigEndian(tempLong); if (tempLong != 0x57415645) { cerr << "Error: expecting the WAVE format (0x57415645 = WAVE) \n" << " but got: " << tempLong << endl; exit(1); } // read the Subchunk1ID (should be "fmt "): sndfile.readBigEndian(tempLong); if (tempLong != 0x666d7420) { cerr << "Error: expecting the fmt subchunk (0x666d7420) \n" << " but got: " << tempLong << endl; exit(1); } // read the Subchunk1Size: sndfile.readLittleEndian(tempLong); if (tempLong != 16) { cerr << "Error: expecting the fmt subchunk size to be 16, \n" << " but it is: " << tempLong << endl; exit(1); } // read the AudioFormat: sndfile.readLittleEndian(tempShort); if (tempShort != 1) { cerr << "Error: expecting audio format to be PCM (1). " << endl << " Cannot handle this format (compression): " << tempShort << endl; exit(1); } // read the NumChannels: sndfile.readLittleEndian(tempShort); if (tempShort > 1000) { cerr << "Error: I don't believe this file has " << tempShort << " channels" << endl; exit(1); } channels = tempShort; // read the SampleRate: sndfile.readLittleEndian(tempLong); samplingRate = tempLong; // read the ByteRate == SampleRate * NumChannels * BitsPerSample/8: sndfile.readLittleEndian(tempLong); // read the BlockAlign == NumChannels * BitsPerSample/8: sndfile.readLittleEndian(tempShort); // read the BitsPerSample: sndfile.readLittleEndian(tempShort); bitsPerSample = tempShort; // read the Subchunk2ID (should be "data" (0x64617461)): sndfile.readBigEndian(tempLong); // read the Subchunk2Size, calculate the number of samples: sndfile.readLittleEndian(tempLong); samples = tempLong / (bitsPerSample/8) / channels; sndfile.close(); // process data elements of SoundHeader // dataLocation = 44; // channels already set // samplingRate already set // sample count already set // bits per sample already set // inputType already set before entering function but set again just in case: inputType = TYPE_WAV_PCM; outputType = inputType; // set the comment string to nothing: if (commentString != NULL) { delete [] commentString; } commentString = new char[1]; commentString[0] = '\0'; commentLength = 0; // set the Next_DataFormat guessNextFormat(); updateDerivedVariables(); } /* * * Wave (*.wav) PCM File Format * * OFFSET SIZE NAME DESCRIPTION * 0 4 ChunkID Contains the letters "RIFF" * 4 4 ChunkSize 38 + ExtraParamSize + SubChunk2Size * This is the offset of the actual sound data * 8 4 Format Contains the letters "WAVE" * 12 4 Subchunk1ID Contains the letters "fmt " * 16 4 Subchunk1Size 18 + ExtraParamSize * 20 2 AudioFormat PCM = 1 (i.e. Linear quantization) * 22 2 NumChannels mono = 1, stereo = 2, etc. * 24 4 SampleRate 8000, 44100, etc. * 28 4 ByteRate == SampleRate * NumChannels * BitsPerSample/8 * 32 2 BlockAlign == NumChannels * BitsPerSample/8 * 34 2 BitsPerSample 8 bits = 8, 16 bits = 16, etc. * 36 4 Subchunk2ID Contains the letters "data" * 40 4 Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8 * the number of data bytes * 44 * Data the actual sound data. * */ ////////////////////////////// // // SoundHeader::updateDerivedVariables // void SoundHeader::updateDerivedVariables(void) { blockAlign = getBitsPerSample()/8 * getChannels(); sampleBytes = getBitsPerSample() / 8; }; // md5sum: 70ce68c99ced33a13c2ea3bb8a33aa64 SoundHeader.cpp [20050403]