📄 snd_wave_source.cpp
字号:
// byte offset in sample database
samplePosition *= m_sampleSize;
// if we are returning some samples, store the pointer
if ( sampleCount )
{
*pData = GetDataPointer() + samplePosition;
Assert( *pData );
}
return sampleCount;
}
// Hardcoded macros to test for zero crossing
#define ZERO_X_8(b) ((b)<2 && (b)>-2)
#define ZERO_X_16(b) ((b)<512 && (b)>-512)
//-----------------------------------------------------------------------------
// Purpose: Search backward for a zero crossing starting at sample
// Input : sample - starting point
// Output : position of zero crossing
//-----------------------------------------------------------------------------
int CAudioSourceMemWave::ZeroCrossingBefore( int sample )
{
char *pWaveData = GetDataPointer();
if ( m_format == WAVE_FORMAT_PCM )
{
if ( m_bits == 8 )
{
char *pData = pWaveData + sample * m_sampleSize;
bool zero = false;
if ( m_channels == 1 )
{
while ( sample > 0 && !zero )
{
if ( ZERO_X_8(*pData) )
zero = true;
else
{
sample--;
pData--;
}
}
}
else
{
while ( sample > 0 && !zero )
{
if ( ZERO_X_8(*pData) && ZERO_X_8(pData[1]) )
zero = true;
else
{
sample--;
pData--;
}
}
}
}
else
{
short *pData = (short *)(pWaveData + sample * m_sampleSize);
bool zero = false;
if ( m_channels == 1 )
{
while ( sample > 0 && !zero )
{
if ( ZERO_X_16(*pData) )
zero = true;
else
{
pData--;
sample--;
}
}
}
else
{
while ( sample > 0 && !zero )
{
if ( ZERO_X_16(*pData) && ZERO_X_16(pData[1]) )
zero = true;
else
{
sample--;
pData--;
}
}
}
}
}
return sample;
}
//-----------------------------------------------------------------------------
// Purpose: Search forward for a zero crossing
// Input : sample - starting point
// Output : position of found zero crossing
//-----------------------------------------------------------------------------
int CAudioSourceMemWave::ZeroCrossingAfter( int sample )
{
char *pWaveData = GetDataPointer();
if ( m_format == WAVE_FORMAT_PCM )
{
if ( m_bits == 8 )
{
char *pData = pWaveData + sample * m_sampleSize;
bool zero = false;
if ( m_channels == 1 )
{
while ( sample < SampleCount() && !zero )
{
if ( ZERO_X_8(*pData) )
zero = true;
else
{
sample++;
pData++;
}
}
}
else
{
while ( sample < SampleCount() && !zero )
{
if ( ZERO_X_8(*pData) && ZERO_X_8(pData[1]) )
zero = true;
else
{
sample++;
pData++;
}
}
}
}
else
{
short *pData = (short *)(pWaveData + sample * m_sampleSize);
bool zero = false;
if ( m_channels == 1 )
{
while ( sample > 0 && !zero )
{
if ( ZERO_X_16(*pData) )
zero = true;
else
{
pData++;
sample++;
}
}
}
else
{
while ( sample > 0 && !zero )
{
if ( ZERO_X_16(*pData) && ZERO_X_16(pData[1]) )
zero = true;
else
{
sample++;
pData++;
}
}
}
}
}
return sample;
}
// This is a CAudioSourceMemWave and gets all of its data from the cache.
class CAudioSourceMemWaveCache : public CAudioSourceMemWave
{
public:
CAudioSourceMemWaveCache( const char *pName );
~CAudioSourceMemWaveCache( void );
virtual void ParseChunk( IterateRIFF &walk, int chunkName );
void ParseDataChunk( IterateRIFF &walk );
bool IsCached( void );
void CacheTouch( void );
void CacheLoad( void );
void CacheUnload( void );
protected:
virtual char *GetDataPointer( void );
cache_user_t m_cache;
private:
CAudioSourceMemWaveCache( const CAudioSourceMemWaveCache & );
};
//-----------------------------------------------------------------------------
// Purpose: NULL the wave data pointer (we haven't loaded yet)
//-----------------------------------------------------------------------------
CAudioSourceMemWaveCache::CAudioSourceMemWaveCache( const char *pName ) :
CAudioSourceMemWave( pName )
{
memset( &m_cache, 0, sizeof(m_cache) );
}
//-----------------------------------------------------------------------------
// Purpose: Free any wave data we've allocated
//-----------------------------------------------------------------------------
CAudioSourceMemWaveCache::~CAudioSourceMemWaveCache( void )
{
CacheUnload();
}
//-----------------------------------------------------------------------------
// Purpose: parse chunks with unique processing to in-memory waves
// Input : &walk - RIFF file
//-----------------------------------------------------------------------------
void CAudioSourceMemWaveCache::ParseChunk( IterateRIFF &walk, int chunkName )
{
switch( chunkName )
{
// this is the audio data
case WAVE_DATA:
{
ParseDataChunk( walk );
}
return;
}
CAudioSourceWave::ParseChunk( walk, chunkName );
}
//-----------------------------------------------------------------------------
// Purpose: reads the actual sample data and parses it
// Input : &walk - RIFF file
//-----------------------------------------------------------------------------
void CAudioSourceMemWaveCache::ParseDataChunk( IterateRIFF &walk )
{
int size = walk.ChunkSize();
// create a buffer for the samples
char *pData = (char *)Cache_Alloc( &m_cache, size, m_pName );
// load them into memory
walk.ChunkRead( pData );
if ( m_format == WAVE_FORMAT_PCM )
{
// number of samples loaded
m_sampleCount = size / m_sampleSize;
// some samples need to be converted
ConvertSamples( pData, m_sampleCount );
}
else if ( m_format == WAVE_FORMAT_ADPCM )
{
// The ADPCM mixers treat the wave source as a flat file of bytes.
m_sampleSize = 1;
// Since each "sample" is a byte (this is a flat file), the number of samples is the file size
m_sampleCount = size;
// file says 4, output is 16
m_bits = 16;
}
}
bool CAudioSourceMemWaveCache::IsCached( void )
{
if ( m_cache.data )
{
if ( Cache_Check( &m_cache ) != NULL )
return true;
}
return false;
}
void CAudioSourceMemWaveCache::CacheTouch( void )
{
IsCached();
}
void CAudioSourceMemWaveCache::CacheLoad( void )
{
if ( IsCached() )
return;
InFileRIFF riff( m_pName, *g_pSndIO );
// UNDONE: Don't use printf to handle errors
if ( riff.RIFFName() != RIFF_WAVE )
{
return;
}
// set up the iterator for the whole file (root RIFF is a chunk)
IterateRIFF walk( riff, riff.RIFFSize() );
while ( walk.ChunkAvailable() )
{
switch( walk.ChunkName() )
{
case WAVE_DATA:
ParseDataChunk( walk );
return;
}
walk.ChunkNext();
}
}
void CAudioSourceMemWaveCache::CacheUnload( void )
{
if ( !m_cache.data )
return;
Cache_Free( &m_cache );
}
char *CAudioSourceMemWaveCache::GetDataPointer( void )
{
char *pData = (char *)Cache_Check( &m_cache );
if ( !pData )
CacheLoad();
return (char *)Cache_Check( &m_cache );
}
//-----------------------------------------------------------------------------
// Purpose: Wave source for streaming wave files
// UNDONE: Handle looping
//-----------------------------------------------------------------------------
class CAudioSourceStreamWave : public CAudioSourceWave, public IWaveStreamSource
{
public:
CAudioSourceStreamWave( const char * );
~CAudioSourceStreamWave();
CAudioMixer *CreateMixer( void );
int GetOutputData( void **pData, int samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] );
void ParseChunk( IterateRIFF &walk, int chunkName );
bool IsStreaming( void ) { return true; }
// IWaveStreamSource
virtual int UpdateLoopingSamplePosition( int samplePosition )
{
return ConvertLoopedPosition( samplePosition );
}
virtual void UpdateSamples( char *pData, int sampleCount )
{
ConvertSamples( pData, sampleCount );
}
private:
CAudioSourceStreamWave( const CAudioSourceStreamWave & ); // not implemented, not accessible
int m_dataStart; // offset of wave data chunk
int m_dataSize; // size of wave data chunk
};
//-----------------------------------------------------------------------------
// Purpose: Save a copy of the file name for instances to open later
// Input : *pFileName - filename
//-----------------------------------------------------------------------------
CAudioSourceStreamWave::CAudioSourceStreamWave( const char *pFileName ) : CAudioSourceWave( pFileName )
{
m_pName = pFileName;
m_dataStart = -1;
m_dataSize = 0;
m_sampleCount = 0;
}
//-----------------------------------------------------------------------------
// Purpose: free the filename buffer
//-----------------------------------------------------------------------------
CAudioSourceStreamWave::~CAudioSourceStreamWave( void )
{
}
//-----------------------------------------------------------------------------
// Purpose: Create an instance (mixer & wavedata) of this sound
// Output : CAudioMixer * - pointer to the mixer
//-----------------------------------------------------------------------------
CAudioMixer *CAudioSourceStreamWave::CreateMixer( void )
{
// BUGBUG: Source constructs the IWaveData, mixer frees it, fix this?
IWaveData *pWaveData = CreateWaveDataStream(*this, static_cast<IWaveStreamSource *>(this), *g_pSndIO, m_pName, m_dataStart, m_dataSize);
if ( pWaveData )
{
CAudioMixer *pMixer = CreateWaveMixer( pWaveData, m_format, m_channels, m_bits );
if ( pMixer )
{
ReferenceAdd( pMixer );
return pMixer;
}
// no mixer, delete the stream buffer/instance
delete pWaveData;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Parse a stream wave file chunk
// unlike the in-memory file, don't load the data, just get a reference to it.
// Input : &walk - RIFF file
//-----------------------------------------------------------------------------
void CAudioSourceStreamWave::ParseChunk( IterateRIFF &walk, int chunkName )
{
// NOTE: It would be nice to break out of parsing once we have the data start and
// save seeking over the whole file. But to do so, we'd have to make
// sure that the CUE chunks occur before the data chunks. In the first
// test file I used, this was not the case.
switch( chunkName )
{
case WAVE_DATA:
{
// data starts at chunk + 8 (chunk name, chunk size = 8 bytes)
m_dataStart = walk.ChunkFilePosition() + 8;
m_dataSize = walk.ChunkSize();
m_sampleCount = m_dataSize / m_sampleSize;
// don't load the data, just know where it is so each instance
// can load it later
}
return;
}
CAudioSourceWave::ParseChunk( walk, chunkName );
}
//-----------------------------------------------------------------------------
// Purpose: This is not implemented here. This source has no data. It is the
// WaveData's responsibility to load/serve the data
//-----------------------------------------------------------------------------
int CAudioSourceStreamWave::GetOutputData( void **pData, int samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] )
{
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Create a wave audio source (streaming or in memory)
// Input : *pName - file name (NOTE: CAUDIOSOURCE KEEPS A POINTER TO pName)
// streaming - if true, don't load, stream each instance
// Output : CAudioSource * - a new source
//-----------------------------------------------------------------------------
CAudioSource *CreateWave( const char *pName, bool streaming )
{
char formatBuffer[1024];
InFileRIFF riff( pName, *g_pSndIO );
// UNDONE: Don't use printf to handle errors
if ( riff.RIFFName() != RIFF_WAVE )
{
static CUtlSymbolTable wavErrors;
CUtlSymbol sym;
sym = wavErrors.Find( pName );
if ( UTL_INVAL_SYMBOL == sym )
{
// See if file exists
if ( g_pFileSystem->FileExists( pName ) )
{
Warning("Bad RIFF file '%s'\n", pName );
}
else
{
Warning("Missing wav file '%s'\n", pName );
}
wavErrors.AddString( pName );
}
return NULL;
}
// set up the iterator for the whole file (root RIFF is a chunk)
IterateRIFF walk( riff, riff.RIFFSize() );
int format = 0;
int formatSize = 0;
// This chunk must be first as it contains the wave's format
// break out when we've parsed it
while ( walk.ChunkAvailable() && format == 0 )
{
switch( walk.ChunkName() )
{
case WAVE_FMT:
{
if ( walk.ChunkSize() <= 1024 )
{
walk.ChunkRead( formatBuffer );
formatSize = walk.ChunkSize();
format = ((WAVEFORMATEX *)formatBuffer)->wFormatTag;
}
}
break;
default:
{
ChunkError( walk.ChunkName() );
}
break;
}
walk.ChunkNext();
}
// Not really a WAVE file or no format chunk, bail
if ( !format )
return NULL;
CAudioSourceWave *pWave;
// create the source from this file
if ( streaming )
pWave = new CAudioSourceStreamWave( pName );
else
pWave = new CAudioSourceMemWaveCache( pName );
// init the wave source
pWave->Setup( formatBuffer, formatSize, walk );
return pWave;
}
//-----------------------------------------------------------------------------
// Purpose: Wrapper for CreateWave()
//-----------------------------------------------------------------------------
CAudioSource *Audio_CreateStreamedWave( const char *pName )
{
if ( Audio_IsMP3( pName ) )
{
return Audio_CreateStreamedMP3( pName );
}
return CreateWave( pName, true );
}
//-----------------------------------------------------------------------------
// Purpose: Wrapper for CreateWave()
//-----------------------------------------------------------------------------
CAudioSource *Audio_CreateMemoryWave( const char *pName )
{
if ( Audio_IsMP3( pName ) )
{
return Audio_CreateMemoryMP3( pName );
}
return CreateWave( pName, false );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -