⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 snd_wave_source.cpp

📁 hl2 source code. Do not use it illegal.
💻 CPP
📖 第 1 页 / 共 2 页
字号:

#define WIN32_LEAN_AND_MEAN
#pragma warning(push, 1)
#include <windows.h>
#include <mmsystem.h>
#include <mmreg.h>			// adpcm format
#pragma warning(pop)

#include <stdio.h>
#include <stdlib.h>

#include "riff.h"
#include "snd_wave_source.h"
#include "snd_wave_mixer_private.h"
#include "snd_audio_source.h"
#include "snd_wave_data.h"
#include "utlbuffer.h"
#include "cache_user.h"

#include "SoundService.h"
#include "snd_io.h"
#include "../../cache.h"
#include "vstdlib/strtools.h"
#include "snd_mp3_source.h"
#include "utlsymbol.h"
#include "filesystem.h"
#include "../../filesystem_engine.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

// #define DEBUG_CHUNKS

#if defined( DEBUG_CHUNKS ) && defined( _DEBUG )

#define WAVE_LIST	MAKEID( 'L', 'I', 'S', 'T' ) 
//-----------------------------------------------------------------------------
// Purpose: Report chunk error
// Input  : id - chunk FOURCC
//-----------------------------------------------------------------------------
void ChunkError( unsigned int id )
{
	// Legacy format, just ignore
	if ( id == WAVE_LIST )
		return;

	char tmp[256];
	char idname[5];
	idname[4] = 0;
	memcpy( idname, &id, 4 );

	Q_snprintf( tmp, sizeof( tmp ), "Unhandled chunk %s\n", idname );
	OutputDebugString( tmp );
}

#else
void ChunkError( unsigned int id )
{
}

#endif


//-----------------------------------------------------------------------------
// Purpose: Init to empty wave
//-----------------------------------------------------------------------------
CAudioSourceWave::CAudioSourceWave( const char *pName )
{
	m_format = 0;
	m_pHeader = NULL;
	// no looping
	m_loopStart = -1;
	m_sampleSize = 1;
	m_sampleCount = 0;

	m_refCount = 0;

	m_pName = pName;
}


CAudioSourceWave::~CAudioSourceWave( void )
{
#if _DEBUG
	if ( !CanDelete() )
		Assert(0);
#endif

	// for non-standard waves, we store a copy of the header in RAM
	delete[] m_pHeader;
}


//-----------------------------------------------------------------------------
// Purpose: Init the wave data.
// Input  : *pHeaderBuffer - the RIFF fmt chunk
//			headerSize - size of that chunk
//-----------------------------------------------------------------------------
void CAudioSourceWave::Init( const char *pHeaderBuffer, int headerSize )
{
	const WAVEFORMATEX *pHeader = (const WAVEFORMATEX *)pHeaderBuffer;

	// copy the relevant header data
	m_format = pHeader->wFormatTag;
	m_bits = pHeader->wBitsPerSample;
	m_rate = pHeader->nSamplesPerSec;
	m_channels = pHeader->nChannels;

	m_sampleSize = (m_bits * m_channels) / 8;
	
	// this can never be zero -- other functions divide by this. 
	// This should never happen, but avoid crashing
	if ( m_sampleSize <= 0 )
		m_sampleSize = 1;

	// For non-standard waves (like ADPCM) store the header, it has some useful data
	if ( m_format != WAVE_FORMAT_PCM )
	{
		m_pHeader = new char[headerSize];
		memcpy( m_pHeader, pHeader, headerSize );
		if ( m_format == WAVE_FORMAT_ADPCM )
		{
			// treat ADPCM sources as a file of bytes.  They are decoded by the mixer
			m_sampleSize = 1;
		}
	}
}


int	CAudioSourceWave::SampleRate( void ) 
{ 
	return m_rate; 
}


//-----------------------------------------------------------------------------
// Purpose: Size of each sample
// Output : 
//-----------------------------------------------------------------------------
int	CAudioSourceWave::SampleSize( void ) 
{ 
	return m_sampleSize; 
}


//-----------------------------------------------------------------------------
// Purpose: Total number of samples in this source
// Output : int
//-----------------------------------------------------------------------------
int CAudioSourceWave::SampleCount( void ) 
{
	if ( m_format == WAVE_FORMAT_ADPCM )
	{
		ADPCMWAVEFORMAT *pFormat = (ADPCMWAVEFORMAT *)m_pHeader;
		int blockSize = ((pFormat->wSamplesPerBlock - 2) * pFormat->wfx.nChannels ) / 2;
		blockSize += 7 * pFormat->wfx.nChannels;

		int blockCount = m_sampleCount / blockSize;
		int blockRem = m_sampleCount % blockSize;
		
		// total samples in complete blocks
		int sampleCount = blockCount * pFormat->wSamplesPerBlock;

		// add remaining in a short block
		if ( blockRem )
		{
			sampleCount += pFormat->wSamplesPerBlock - (((blockSize - blockRem) * 2) / m_channels);
		}
		return sampleCount;
	}
	return m_sampleCount; 
}


bool CAudioSourceWave::IsVoiceSource()
{
	if ( GetSentence() )
	{
		if ( GetSentence()->GetVoiceDuck() )
			return true;
	}
	return false;
}


//-----------------------------------------------------------------------------
// Purpose: Do any sample conversion
//			For 8 bit PCM, convert to signed because the mixing routine assumes this
// Input  : *pData - pointer to sample data
//			sampleCount - number of samples
//-----------------------------------------------------------------------------
void CAudioSourceWave::ConvertSamples( char *pData, int sampleCount )
{
	if ( m_format == WAVE_FORMAT_PCM )
	{
		if ( m_bits == 8 )
		{
			for ( int i = 0; i < sampleCount; i++ )
			{
				for ( int j = 0; j < m_channels; j++ )
				{
					*pData = (unsigned char)((int)((unsigned)*pData) - 128);
					pData++;
				}
			}
		}
	}
}



//-----------------------------------------------------------------------------
// Purpose: Parse base chunks
// Input  : &walk - riff file to parse
//		  : chunkName - name of the chunk to parse
//-----------------------------------------------------------------------------
// UNDONE: Move parsing loop here and drop each chunk into a virtual function
//			instead of this being virtual.
void CAudioSourceWave::ParseChunk( IterateRIFF &walk, int chunkName )
{
	switch( chunkName )
	{
		case WAVE_CUE:
			{
				ParseCueChunk( walk );
			}
			break;
		case WAVE_SAMPLER:
			{
				ParseSamplerChunk( walk );
			}
			break;
		case WAVE_VALVEDATA:
			{
				ParseSentence( walk );
			}
			break;

		case WAVE_FACT:
			break;

			// unknown/don't care
		default:
			{
				ChunkError( walk.ChunkName() );
			}
			break;
	}
}

bool CAudioSourceWave::IsLooped( void ) 
{ 
	return (m_loopStart >= 0) ? true : false; 
}

bool CAudioSourceWave::IsStereoWav( void ) 
{ 
	return (m_channels == 2) ? true : false;
}

bool CAudioSourceWave::IsStreaming( void ) 
{ 
	return false; 
}


bool CAudioSourceWave::IsCached( void )
{
	return true;
}


void CAudioSourceWave::CacheTouch( void )
{
	IsCached();
}


void CAudioSourceWave::CacheLoad( void )
{
}

void CAudioSourceWave::CacheUnload( void )
{
}


int	CAudioSourceWave::ZeroCrossingBefore( int sample )
{
	return sample;
}


int	CAudioSourceWave::ZeroCrossingAfter( int sample )
{
	return sample;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : &walk - 
//-----------------------------------------------------------------------------
void CAudioSourceWave::ParseSentence( IterateRIFF &walk )
{
	CUtlBuffer buf( 0, 0, true );

	buf.EnsureCapacity( walk.ChunkSize() );
	walk.ChunkRead( buf.Base() );
	buf.SeekPut( CUtlBuffer::SEEK_HEAD, walk.ChunkSize() );

	m_Sentence.InitFromDataChunk( buf.Base(), buf.TellPut() );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : CSentence
//-----------------------------------------------------------------------------
CSentence *CAudioSourceWave::GetSentence( void )
{
	return &m_Sentence;
}

//-----------------------------------------------------------------------------
// Purpose: Bastardized construction routine.  This is just to avoid complex
//			constructor functions so code can be shared more easily by sub-classes
// Input  : *pFormatBuffer - RIFF header
//			formatSize - header size
//			&walk - RIFF file
//-----------------------------------------------------------------------------
void CAudioSourceWave::Setup( const char *pFormatBuffer, int formatSize, IterateRIFF &walk )
{
	Init( pFormatBuffer, formatSize );

	while ( walk.ChunkAvailable() )
	{
		ParseChunk( walk, walk.ChunkName() );
		walk.ChunkNext();
	}
}


//-----------------------------------------------------------------------------
// Purpose: parses loop information from a cue chunk
// Input  : &walk - RIFF iterator
// Output : int loop start position
//-----------------------------------------------------------------------------
void CAudioSourceWave::ParseCueChunk( IterateRIFF &walk )
{
	// Cue chunk as specified by RIFF format
	// see $/research/jay/sound/riffnew.htm
	struct 
	{
		unsigned int dwName; 
		unsigned int dwPosition;
		unsigned int fccChunk;
		unsigned int dwChunkStart;
		unsigned int dwBlockStart; 
		unsigned int dwSampleOffset;
	} cue_chunk;

	int cueCount;

	// assume that the cue chunk stored in the wave is the start of the loop
	// assume only one cue chunk, UNDONE: Test this assumption here?
	cueCount = walk.ChunkReadInt();

	walk.ChunkReadPartial( &cue_chunk, sizeof(cue_chunk) );
	m_loopStart = cue_chunk.dwSampleOffset;
}

//-----------------------------------------------------------------------------
// Purpose: parses loop information from a 'smpl' chunk
// Input  : &walk - RIFF iterator
// Output : int loop start position
//-----------------------------------------------------------------------------
void CAudioSourceWave::ParseSamplerChunk( IterateRIFF &walk )
{
	// Sampler chunk for MIDI instruments
	// Parse loop info from this chunk too
	struct SampleLoop
	{
		unsigned int	dwIdentifier;
		unsigned int	dwType;
		unsigned int	dwStart;
		unsigned int	dwEnd;
		unsigned int	dwFraction;
		unsigned int	dwPlayCount;
	};

	struct 
	{
		unsigned int	dwManufacturer;
		unsigned int	dwProduct;
		unsigned int	dwSamplePeriod;
		unsigned int	dwMIDIUnityNote;
		unsigned int	dwMIDIPitchFraction;
		unsigned int	dwSMPTEFormat;
		unsigned int	dwSMPTEOffset;
		unsigned int	cSampleLoops;
		unsigned int	cbSamplerData;
		struct SampleLoop Loops[1];
	} samplerChunk;

	// assume that the loop end is the sample end
	// assume that only the first loop is relevant

	walk.ChunkReadPartial( &samplerChunk, sizeof(samplerChunk) );

	// only support normal forward loops
	if ( samplerChunk.Loops[0].dwType == 0 )
	{
		Assert( samplerChunk.cSampleLoops > 0 );
		m_loopStart = samplerChunk.Loops[0].dwStart;
	}
#if _DEBUG
	else
	{
		Msg("Unknown sampler chunk type %d\n", samplerChunk.Loops[0].dwType );
	}
#endif
}


//-----------------------------------------------------------------------------
// Purpose: get the wave header
//-----------------------------------------------------------------------------
void *CAudioSourceWave::GetHeader( void )
{
	return m_pHeader;
}


//-----------------------------------------------------------------------------
// Purpose: wrap the position wrt looping
// Input  : samplePosition - absolute position
// Output : int - looped position
//-----------------------------------------------------------------------------
int CAudioSourceWave::ConvertLoopedPosition( int samplePosition )
{
	// if the wave is looping and we're past the end of the sample
	// convert to a position within the loop
	// At the end of the loop, we return a short buffer, and subsequent call
	// will loop back and get the rest of the buffer
	if ( m_loopStart >= 0 && samplePosition >= m_sampleCount )
	{
		// size of loop
		int loopSize = m_sampleCount - m_loopStart;
		// subtract off starting bit of the wave
		samplePosition -= m_loopStart;
		
		if ( loopSize )
		{
			// "real" position in memory (mod off extra loops)
			samplePosition = m_loopStart + (samplePosition % loopSize);
		}
		// ERROR? if no loopSize
	}

	return samplePosition;
}

//-----------------------------------------------------------------------------
// Purpose: remove the reference for the mixer getting deleted
// Input  : *pMixer - 
//-----------------------------------------------------------------------------
void CAudioSourceWave::ReferenceRemove( CAudioMixer *pMixer )
{
	m_refCount--;
}


//-----------------------------------------------------------------------------
// Purpose: Add a mixer reference
// Input  : *pMixer - 
//-----------------------------------------------------------------------------
void CAudioSourceWave::ReferenceAdd( CAudioMixer *pMixer )
{
	m_refCount++;
}


//-----------------------------------------------------------------------------
// Purpose: return true if no mixers reference this source
//-----------------------------------------------------------------------------
bool CAudioSourceWave::CanDelete( void )
{
	if ( m_refCount > 0 )
		return false;

	return true;
}


// CAudioSourceMemWave is a bunch of wave data that is all in memory.
// To use it:
// - derive from CAudioSourceMemWave
// - call CAudioSourceWave::Init with a WAVEFORMATEX
// - set m_sampleCount.
// - implement GetDataPointer
class CAudioSourceMemWave : public CAudioSourceWave
{
public:
							CAudioSourceMemWave();
							CAudioSourceMemWave( char const* pName );

	// These are all implemented by CAudioSourceMemWave.
	virtual CAudioMixer*	CreateMixer( void );
	virtual int				GetOutputData( void **pData, int samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] );
	virtual int				ZeroCrossingBefore( int sample );
	virtual int				ZeroCrossingAfter( int sample );

protected:								   
	
	// Whoeover derives must implement this.
	virtual char	*GetDataPointer( void )=0;

private:
	CAudioSourceMemWave( const CAudioSourceMemWave & ); // not implemented, not accessible
};


CAudioSourceMemWave::CAudioSourceMemWave() :
	CAudioSourceWave("")
{
}

CAudioSourceMemWave::CAudioSourceMemWave( char const* pName ) : 
	CAudioSourceWave(pName)
{
}

//-----------------------------------------------------------------------------
// Purpose: Creates a mixer and initializes it with an appropriate mixer
//-----------------------------------------------------------------------------
CAudioMixer *CAudioSourceMemWave::CreateMixer( void )
{
	CAudioMixer *pMixer = CreateWaveMixer( CreateWaveDataMemory(*this), m_format, m_channels, m_bits );
	ReferenceAdd( pMixer );

	return pMixer;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : **pData - output pointer to samples
//			samplePosition - position (in samples not bytes) 
//			sampleCount - number of samples (not bytes)
// Output : int - number of samples available
//-----------------------------------------------------------------------------
int CAudioSourceMemWave::GetOutputData( void **pData, int samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] )
{
	// handle position looping
	samplePosition = ConvertLoopedPosition( samplePosition );

	// how many samples are available (linearly not counting looping)
	int totalSampleCount = m_sampleCount - samplePosition;

	// may be asking for a sample out of range, clip at zero
	if ( totalSampleCount < 0 )
		totalSampleCount = 0;

	// clip max output samples to max available
	if ( sampleCount > totalSampleCount )
		sampleCount = totalSampleCount;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -