source-engine/tier2/soundutils.cpp
2022-06-05 01:13:13 +03:00

238 lines
5.9 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Helper methods + classes for sound
//
//===========================================================================//
#include "tier2/soundutils.h"
#include "tier2/riff.h"
#include "tier2/tier2.h"
#include "filesystem.h"
#ifdef IS_WINDOWS_PC
#include <windows.h> // WAVEFORMATEX, WAVEFORMAT and ADPCM WAVEFORMAT!!!
#include <mmreg.h>
#else
#ifdef _X360
#include "xbox/xbox_win32stubs.h" // WAVEFORMATEX, WAVEFORMAT and ADPCM WAVEFORMAT!!!
#endif
#endif
//-----------------------------------------------------------------------------
// RIFF reader/writers that use the file system
//-----------------------------------------------------------------------------
class CFSIOReadBinary : public IFileReadBinary
{
public:
// inherited from IFileReadBinary
virtual intp open( const char *pFileName );
virtual int read( void *pOutput, int size, intp file );
virtual void seek( intp file, int pos );
virtual unsigned int tell( intp file );
virtual unsigned int size( intp file );
virtual void close( intp file );
};
class CFSIOWriteBinary : public IFileWriteBinary
{
public:
virtual intp create( const char *pFileName );
virtual int write( void *pData, int size, intp file );
virtual void close( intp file );
virtual void seek( intp file, int pos );
virtual unsigned int tell( intp file );
};
//-----------------------------------------------------------------------------
// Singletons
//-----------------------------------------------------------------------------
static CFSIOReadBinary s_FSIoIn;
static CFSIOWriteBinary s_FSIoOut;
IFileReadBinary *g_pFSIOReadBinary = &s_FSIoIn;
IFileWriteBinary *g_pFSIOWriteBinary = &s_FSIoOut;
//-----------------------------------------------------------------------------
// RIFF reader that use the file system
//-----------------------------------------------------------------------------
intp CFSIOReadBinary::open( const char *pFileName )
{
return (intp)g_pFullFileSystem->Open( pFileName, "rb" );
}
int CFSIOReadBinary::read( void *pOutput, int size, intp file )
{
if ( !file )
return 0;
return g_pFullFileSystem->Read( pOutput, size, (FileHandle_t)file );
}
void CFSIOReadBinary::seek( intp file, int pos )
{
if ( !file )
return;
g_pFullFileSystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD );
}
unsigned int CFSIOReadBinary::tell( intp file )
{
if ( !file )
return 0;
return g_pFullFileSystem->Tell( (FileHandle_t)file );
}
unsigned int CFSIOReadBinary::size( intp file )
{
if ( !file )
return 0;
return g_pFullFileSystem->Size( (FileHandle_t)file );
}
void CFSIOReadBinary::close( intp file )
{
if ( !file )
return;
g_pFullFileSystem->Close( (FileHandle_t)file );
}
//-----------------------------------------------------------------------------
// RIFF writer that use the file system
//-----------------------------------------------------------------------------
intp CFSIOWriteBinary::create( const char *pFileName )
{
g_pFullFileSystem->SetFileWritable( pFileName, true );
return (intp)g_pFullFileSystem->Open( pFileName, "wb" );
}
int CFSIOWriteBinary::write( void *pData, int size, intp file )
{
return g_pFullFileSystem->Write( pData, size, (FileHandle_t)file );
}
void CFSIOWriteBinary::close( intp file )
{
g_pFullFileSystem->Close( (FileHandle_t)file );
}
void CFSIOWriteBinary::seek( intp file, int pos )
{
g_pFullFileSystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD );
}
unsigned int CFSIOWriteBinary::tell( intp file )
{
return g_pFullFileSystem->Tell( (FileHandle_t)file );
}
#ifndef POSIX
//-----------------------------------------------------------------------------
// Returns the duration of a wav file
//-----------------------------------------------------------------------------
float GetWavSoundDuration( const char *pWavFile )
{
InFileRIFF riff( pWavFile, *g_pFSIOReadBinary );
// UNDONE: Don't use printf to handle errors
if ( riff.RIFFName() != RIFF_WAVE )
return 0.0f;
int nDataSize = 0;
// set up the iterator for the whole file (root RIFF is a chunk)
IterateRIFF walk( riff, riff.RIFFSize() );
// This chunk must be first as it contains the wave's format
// break out when we've parsed it
char pFormatBuffer[ 1024 ];
int nFormatSize;
bool bFound = false;
for ( ; walk.ChunkAvailable( ); walk.ChunkNext() )
{
switch ( walk.ChunkName() )
{
case WAVE_FMT:
bFound = true;
if ( walk.ChunkSize() > sizeof(pFormatBuffer) )
{
Warning( "oops, format tag too big!!!" );
return 0.0f;
}
nFormatSize = walk.ChunkSize();
walk.ChunkRead( pFormatBuffer );
break;
case WAVE_DATA:
nDataSize += walk.ChunkSize();
break;
}
}
if ( !bFound )
return 0.0f;
const WAVEFORMATEX *pHeader = (const WAVEFORMATEX *)pFormatBuffer;
int format = pHeader->wFormatTag;
int nBits = pHeader->wBitsPerSample;
int nRate = pHeader->nSamplesPerSec;
int nChannels = pHeader->nChannels;
int nSampleSize = ( nBits * nChannels ) / 8;
// this can never be zero -- other functions divide by this.
// This should never happen, but avoid crashing
if ( nSampleSize <= 0 )
{
nSampleSize = 1;
}
int nSampleCount = 0;
float flTrueSampleSize = nSampleSize;
if ( format == WAVE_FORMAT_ADPCM )
{
nSampleSize = 1;
ADPCMWAVEFORMAT *pFormat = (ADPCMWAVEFORMAT *)pFormatBuffer;
int blockSize = ((pFormat->wSamplesPerBlock - 2) * pFormat->wfx.nChannels ) / 2;
blockSize += 7 * pFormat->wfx.nChannels;
int blockCount = nSampleCount / blockSize;
int blockRem = nSampleCount % blockSize;
// total samples in complete blocks
nSampleCount = blockCount * pFormat->wSamplesPerBlock;
// add remaining in a short block
if ( blockRem )
{
nSampleCount += pFormat->wSamplesPerBlock - (((blockSize - blockRem) * 2) / nChannels);
}
flTrueSampleSize = 0.5f;
}
else
{
nSampleCount = nDataSize / nSampleSize;
}
float flDuration = (float)nSampleCount / (float)nRate;
return flDuration;
}
#endif