/* =========================================================================== Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). Doom 3 Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 Source Code 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 Source Code. If not, see . In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "sys/platform.h" #include "framework/FileSystem.h" #include "sound/snd_local.h" //----------------------------------------------------------------------------- // Name: idWaveFile::idWaveFile() // Desc: Constructs the class. Call Open() to open a wave file for reading. // Then call Read() as needed. Calling the destructor or Close() // will close the file. //----------------------------------------------------------------------------- idWaveFile::idWaveFile( void ) { memset( &mpwfx, 0, sizeof( waveformatextensible_t ) ); mhmmio = NULL; mdwSize = 0; mseekBase = 0; mbIsReadingFromMemory = false; mpbData = NULL; ogg = NULL; isOgg = false; } //----------------------------------------------------------------------------- // Name: idWaveFile::~idWaveFile() // Desc: Destructs the class //----------------------------------------------------------------------------- idWaveFile::~idWaveFile( void ) { Close(); if ( mbIsReadingFromMemory && mpbData ) { Mem_Free( mpbData ); } memset( &mpwfx, 0, sizeof( waveformatextensible_t ) ); } //----------------------------------------------------------------------------- // Name: idWaveFile::Open() // Desc: Opens a wave file for reading //----------------------------------------------------------------------------- int idWaveFile::Open( const char* strFileName, waveformatex_t* pwfx ) { mbIsReadingFromMemory = false; mpbData = NULL; mpbDataCur = mpbData; if( strFileName == NULL ) { return -1; } idStr name = strFileName; // note: used to only check for .wav when making a build name.SetFileExtension( ".ogg" ); if ( fileSystem->ReadFile( name, NULL, NULL ) != -1 ) { return OpenOGG( name, pwfx ); } memset( &mpwfx, 0, sizeof( waveformatextensible_t ) ); mhmmio = fileSystem->OpenFileRead( strFileName ); if ( !mhmmio ) { mdwSize = 0; return -1; } if ( mhmmio->Length() <= 0 ) { mhmmio = NULL; return -1; } if ( ReadMMIO() != 0 ) { // ReadMMIO will fail if its an not a wave file Close(); return -1; } mfileTime = mhmmio->Timestamp(); if ( ResetFile() != 0 ) { Close(); return -1; } // After the reset, the size of the wav file is mck.cksize so store it now mdwSize = mck.cksize / sizeof( short ); mMemSize = mck.cksize; if ( mck.cksize != 0xffffffff ) { if ( pwfx ) { memcpy( pwfx, (waveformatex_t *)&mpwfx, sizeof(waveformatex_t)); } return 0; } return -1; } //----------------------------------------------------------------------------- // Name: idWaveFile::OpenFromMemory() // Desc: copy data to idWaveFile member variable from memory //----------------------------------------------------------------------------- int idWaveFile::OpenFromMemory( short* pbData, int ulDataSize, waveformatextensible_t* pwfx ) { mpwfx = *pwfx; mulDataSize = ulDataSize; mpbData = pbData; mpbDataCur = mpbData; mdwSize = ulDataSize / sizeof( short ); mMemSize = ulDataSize; mbIsReadingFromMemory = true; return 0; } //----------------------------------------------------------------------------- // Name: idWaveFile::ReadMMIO() // Desc: Support function for reading from a multimedia I/O stream. // mhmmio must be valid before calling. This function uses it to // update mckRiff, and mpwfx. //----------------------------------------------------------------------------- int idWaveFile::ReadMMIO( void ) { mminfo_t ckIn; // chunk info. for general use. pcmwaveformat_t pcmWaveFormat; // Temp PCM structure to load in. memset( &mpwfx, 0, sizeof( waveformatextensible_t ) ); mhmmio->Read( &mckRiff, 12 ); assert( !isOgg ); mckRiff.ckid = LittleInt( mckRiff.ckid ); mckRiff.cksize = LittleInt( mckRiff.cksize ); mckRiff.fccType = LittleInt( mckRiff.fccType ); mckRiff.dwDataOffset = 12; // Check to make sure this is a valid wave file if( (mckRiff.ckid != fourcc_riff) || (mckRiff.fccType != mmioFOURCC('W', 'A', 'V', 'E') ) ) { return -1; } // Search the input file for for the 'fmt ' chunk. ckIn.dwDataOffset = 12; do { if (8 != mhmmio->Read( &ckIn, 8 ) ) { return -1; } assert( !isOgg ); ckIn.ckid = LittleInt( ckIn.ckid ); ckIn.cksize = LittleInt( ckIn.cksize ); ckIn.dwDataOffset += ckIn.cksize-8; } while (ckIn.ckid != mmioFOURCC('f', 'm', 't', ' ')); // Expect the 'fmt' chunk to be at least as large as ; // if there are extra parameters at the end, we'll ignore them if( ckIn.cksize < sizeof(pcmwaveformat_t) ) { return -1; } // Read the 'fmt ' chunk into . if( mhmmio->Read( &pcmWaveFormat, sizeof(pcmWaveFormat) ) != sizeof(pcmWaveFormat) ) { return -1; } assert( !isOgg ); pcmWaveFormat.wf.wFormatTag = LittleShort( pcmWaveFormat.wf.wFormatTag ); pcmWaveFormat.wf.nChannels = LittleShort( pcmWaveFormat.wf.nChannels ); pcmWaveFormat.wf.nSamplesPerSec = LittleInt( pcmWaveFormat.wf.nSamplesPerSec ); pcmWaveFormat.wf.nAvgBytesPerSec = LittleInt( pcmWaveFormat.wf.nAvgBytesPerSec ); pcmWaveFormat.wf.nBlockAlign = LittleShort( pcmWaveFormat.wf.nBlockAlign ); pcmWaveFormat.wBitsPerSample = LittleShort( pcmWaveFormat.wBitsPerSample ); // Copy the bytes from the pcm structure to the waveformatex_t structure memcpy( &mpwfx, &pcmWaveFormat, sizeof(pcmWaveFormat) ); // Allocate the waveformatex_t, but if its not pcm format, read the next // word, and thats how many extra bytes to allocate. if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_TAG_PCM ) { mpwfx.Format.cbSize = 0; } else { return -1; // we don't handle these (32 bit wavefiles, etc) #if 0 // Read in length of extra bytes. word cbExtraBytes = 0L; if( mhmmio->Read( (char*)&cbExtraBytes, sizeof(word) ) != sizeof(word) ) return -1; mpwfx.Format.cbSize = cbExtraBytes; // Now, read those extra bytes into the structure, if cbExtraAlloc != 0. if( mhmmio->Read( (char*)(((byte*)&(mpwfx.Format.cbSize))+sizeof(word)), cbExtraBytes ) != cbExtraBytes ) { memset( &mpwfx, 0, sizeof( waveformatextensible_t ) ); return -1; } #endif } return 0; } //----------------------------------------------------------------------------- // Name: idWaveFile::ResetFile() // Desc: Resets the internal mck pointer so reading starts from the // beginning of the file again //----------------------------------------------------------------------------- int idWaveFile::ResetFile( void ) { if( mbIsReadingFromMemory ) { mpbDataCur = mpbData; } else { if( mhmmio == NULL ) { return -1; } // Seek to the data if( -1 == mhmmio->Seek( mckRiff.dwDataOffset + sizeof(fourcc), FS_SEEK_SET ) ) { return -1; } // Search the input file for for the 'fmt ' chunk. mck.ckid = 0; do { byte ioin; if ( !mhmmio->Read( &ioin, 1 ) ) { return -1; } mck.ckid = (mck.ckid>>8) | (ioin<<24); } while (mck.ckid != mmioFOURCC('d', 'a', 't', 'a')); mhmmio->Read( &mck.cksize, 4 ); assert( !isOgg ); mck.cksize = LittleInt( mck.cksize ); mseekBase = mhmmio->Tell(); } return 0; } //----------------------------------------------------------------------------- // Name: idWaveFile::Read() // Desc: Reads section of data from a wave file into pBuffer and returns // how much read in pdwSizeRead, reading not more than dwSizeToRead. // This uses mck to determine where to start reading from. So // subsequent calls will be continue where the last left off unless // Reset() is called. //----------------------------------------------------------------------------- int idWaveFile::Read( byte* pBuffer, int dwSizeToRead, int *pdwSizeRead ) { if ( ogg != NULL ) { return ReadOGG( pBuffer, dwSizeToRead, pdwSizeRead ); } else if ( mbIsReadingFromMemory ) { if( mpbDataCur == NULL ) { return -1; } if( (byte*)(mpbDataCur + dwSizeToRead) > (byte*)(mpbData + mulDataSize) ) { dwSizeToRead = mulDataSize - (int)(mpbDataCur - mpbData); } SIMDProcessor->Memcpy( pBuffer, mpbDataCur, dwSizeToRead ); mpbDataCur += dwSizeToRead; if ( pdwSizeRead != NULL ) { *pdwSizeRead = dwSizeToRead; } return dwSizeToRead; } else { if( mhmmio == NULL ) { return -1; } if( pBuffer == NULL ) { return -1; } dwSizeToRead = mhmmio->Read( pBuffer, dwSizeToRead ); // this is hit by ogg code, which does it's own byte swapping internally if ( !isOgg ) { LittleRevBytes( pBuffer, 2, dwSizeToRead / 2 ); } if ( pdwSizeRead != NULL ) { *pdwSizeRead = dwSizeToRead; } return dwSizeToRead; } } //----------------------------------------------------------------------------- // Name: idWaveFile::Close() // Desc: Closes the wave file //----------------------------------------------------------------------------- int idWaveFile::Close( void ) { if ( ogg != NULL ) { return CloseOGG(); } if( mhmmio != NULL ) { fileSystem->CloseFile( mhmmio ); mhmmio = NULL; } return 0; } //----------------------------------------------------------------------------- // Name: idWaveFile::Seek() //----------------------------------------------------------------------------- int idWaveFile::Seek( int offset ) { if ( ogg != NULL ) { common->FatalError( "idWaveFile::Seek: cannot seek on an OGG file\n" ); } else if( mbIsReadingFromMemory ) { mpbDataCur = mpbData + offset; } else { if( mhmmio == NULL ) { return -1; } if ((int)(offset+mseekBase) == mhmmio->Tell()) { return 0; } mhmmio->Seek( offset + mseekBase, FS_SEEK_SET ); return 0; } return -1; }