//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $Date: $ // //----------------------------------------------------------------------------- // $Log: $ // // $NoKeywords: $ //=============================================================================// #ifndef SND_AUDIO_SOURCE_H #define SND_AUDIO_SOURCE_H #pragma once #if !defined( _X360 ) #define MP3_SUPPORT 1 #endif #define AUDIOSOURCE_COPYBUF_SIZE 4096 struct channel_t; class CSentence; class CSfxTable; class CAudioSource; class IAudioDevice; class CUtlBuffer; #include "tier0/vprof.h" //----------------------------------------------------------------------------- // Purpose: This is an instance of an audio source. // Mixers are attached to channels and reference an audio source. // Mixers are specific to the sample format and source format. // Mixers are never re-used, so they can track instance data like // sample position, fractional sample, stream cache, faders, etc. //----------------------------------------------------------------------------- abstract_class CAudioMixer { public: virtual ~CAudioMixer( void ) {} // return number of samples mixed virtual int MixDataToDevice( IAudioDevice *pDevice, channel_t *pChannel, int sampleCount, int outputRate, int outputOffset ) = 0; virtual int SkipSamples( channel_t *pChannel, int sampleCount, int outputRate, int outputOffset ) = 0; virtual bool ShouldContinueMixing( void ) = 0; virtual CAudioSource *GetSource( void ) = 0; // get the current position (next sample to be mixed) virtual int GetSamplePosition( void ) = 0; // Allow the mixer to modulate pitch and volume. // returns a floating point modulator virtual float ModifyPitch( float pitch ) = 0; virtual float GetVolumeScale( void ) = 0; // NOTE: Playback is optimized for linear streaming. These calls will usually cost performance // It is currently optimal to call them before any playback starts, but some audio sources may not // guarantee this. Also, some mixers may choose to ignore these calls for internal reasons (none do currently). // Move the current position to newPosition // BUGBUG: THIS CALL DOES NOT SUPPORT MOVING BACKWARD, ONLY FORWARD!!! virtual void SetSampleStart( int newPosition ) = 0; // End playback at newEndPosition virtual void SetSampleEnd( int newEndPosition ) = 0; // How many samples to skip before commencing actual data reading ( to allow sub-frametime sound // offsets and avoid synchronizing sounds to various 100 msec clock intervals throughout the // engine and game code) virtual void SetStartupDelaySamples( int delaySamples ) = 0; virtual int GetMixSampleSize() = 0; // Certain async loaded sounds lazilly load into memory in the background, use this to determine // if the sound is ready for mixing virtual bool IsReadyToMix() = 0; // NOTE: The "saved" position can be different than the "sample" position // NOTE: Allows mixer to save file offsets, loop info, etc virtual int GetPositionForSave() = 0; virtual void SetPositionFromSaved( int savedPosition ) = 0; }; inline int CalcSampleSize( int bitsPerSample, int _channels ) { return (bitsPerSample >> 3) * _channels; } #include "UtlCachedFileData.h" class CSentence; class CSfxTable; class CAudioSourceCachedInfo : public IBaseCacheInfo { public: CAudioSourceCachedInfo(); CAudioSourceCachedInfo( const CAudioSourceCachedInfo& src ); virtual ~CAudioSourceCachedInfo(); CAudioSourceCachedInfo& operator =( const CAudioSourceCachedInfo& src ); void Clear(); void RemoveData(); virtual void Save( CUtlBuffer& buf ); virtual void Restore( CUtlBuffer& buf ); virtual void Rebuild( char const *filename ); // A hack, but will work okay static int s_CurrentType; static CSfxTable *s_pSfx; static bool s_bIsPrecacheSound; inline int Type() const { return info.m_Type; } void SetType( int type ) { info.m_Type = type; } inline int Bits() const { return info.m_bits; } void SetBits( int bits ) { info.m_bits = bits; } inline int Channels() const { return info.m_channels; } void SetChannels( int _channels ) { info.m_channels = _channels; } inline int SampleSize() const { return info.m_sampleSize; } void SetSampleSize( int size ) { info.m_sampleSize = size; } inline int Format() const { return info.m_format; } void SetFormat( int format ) { info.m_format = format; } inline int SampleRate() const { return info.m_rate; } void SetSampleRate( int rate ) { info.m_rate = rate; } inline int CachedDataSize() const { return (int)m_usCachedDataSize; } void SetCachedDataSize( int size ) { m_usCachedDataSize = (unsigned short)size; } inline const byte *CachedData() const { return m_pCachedData; } void SetCachedData( const byte *data ) { m_pCachedData = ( byte * )data; flags.m_bCachedData = ( data != NULL ) ? true : false; } inline int HeaderSize() const { return (int)m_usHeaderSize; } void SetHeaderSize( int size ) { m_usHeaderSize = (unsigned short)size; } inline const byte *HeaderData() const { return m_pHeader; } void SetHeaderData( const byte *data ) { m_pHeader = ( byte * )data; flags.m_bHeader = ( data != NULL ) ? true : false; } inline int LoopStart() const { return m_loopStart; } void SetLoopStart( int start ) { m_loopStart = start; } inline int SampleCount() const { return m_sampleCount; } void SetSampleCount( int count ) { m_sampleCount = count; } inline int DataStart() const { return m_dataStart; } void SetDataStart( int start ) { m_dataStart = start; } inline int DataSize() const { return m_dataSize; } void SetDataSize( int size ) { m_dataSize = size; } inline CSentence *Sentence() const { return m_pSentence; } void SetSentence( CSentence *sentence ) { m_pSentence = sentence; flags.m_bSentence = ( sentence != NULL ) ? true : false; } private: union { unsigned int infolong; struct { unsigned int m_Type : 2; // 0 1 2 or 3 unsigned int m_bits : 5; // 0 to 31 unsigned int m_channels : 2; // 1 or 2 unsigned int m_sampleSize : 3; // 1 2 or 4 unsigned int m_format : 2; // 1 == PCM, 2 == ADPCM unsigned int m_rate : 17; // 0 to 64 K } info; }; union { byte flagsbyte; struct { bool m_bSentence : 1; bool m_bCachedData : 1; bool m_bHeader : 1; } flags; }; int m_loopStart; int m_sampleCount; int m_dataStart; // offset of wave data chunk int m_dataSize; // size of wave data chunk unsigned short m_usCachedDataSize; unsigned short m_usHeaderSize; CSentence *m_pSentence; byte *m_pCachedData; byte *m_pHeader; }; class IAudioSourceCache { public: virtual bool Init( unsigned int memSize ) = 0; virtual void Shutdown() = 0; virtual void LevelInit( char const *mapname ) = 0; virtual void LevelShutdown() = 0; // This invalidates the cached size/date info for sounds so it'll regenerate that next time it's accessed. // Used when you connect to a pure server. virtual void ForceRecheckDiskInfo() = 0; virtual CAudioSourceCachedInfo *GetInfo( int audiosourcetype, bool soundisprecached, CSfxTable *sfx ) = 0; virtual void RebuildCacheEntry( int audiosourcetype, bool soundisprecached, CSfxTable *sfx ) = 0; }; extern IAudioSourceCache *audiosourcecache; FORWARD_DECLARE_HANDLE( memhandle_t ); typedef int StreamHandle_t; enum { INVALID_STREAM_HANDLE = (StreamHandle_t)~0 }; typedef int BufferHandle_t; enum { INVALID_BUFFER_HANDLE = (BufferHandle_t)~0 }; typedef unsigned int streamFlags_t; enum { STREAMED_FROMDVD = 0x00000001, // stream buffers are compliant to dvd sectors STREAMED_SINGLEPLAY = 0x00000002, // non recurring data, buffers don't need to persist and can be recycled STREAMED_QUEUEDLOAD = 0x00000004, // hint the streamer to load using the queued loader system }; abstract_class IAsyncWavDataCache { public: virtual bool Init( unsigned int memSize ) = 0; virtual void Shutdown() = 0; // implementation that treats file as monolithic virtual memhandle_t AsyncLoadCache( char const *filename, int datasize, int startpos, bool bIsPrefetch = false ) = 0; virtual void PrefetchCache( char const *filename, int datasize, int startpos ) = 0; virtual bool CopyDataIntoMemory( char const *filename, int datasize, int startpos, void *buffer, int bufsize, int copystartpos, int bytestocopy, bool *pbPostProcessed ) = 0; virtual bool CopyDataIntoMemory( memhandle_t& handle, char const *filename, int datasize, int startpos, void *buffer, int bufsize, int copystartpos, int bytestocopy, bool *pbPostProcessed ) = 0; virtual bool IsDataLoadCompleted( memhandle_t handle, bool *pIsValid ) = 0; virtual void RestartDataLoad( memhandle_t *pHandle, const char *pFilename, int dataSize, int startpos ) = 0; virtual bool GetDataPointer( memhandle_t& handle, char const *filename, int datasize, int startpos, void **pData, int copystartpos, bool *pbPostProcessed ) = 0; virtual void SetPostProcessed( memhandle_t handle, bool proc ) = 0; virtual void Unload( memhandle_t handle ) = 0; // alternate multi-buffer streaming implementation virtual StreamHandle_t OpenStreamedLoad( char const *pFileName, int dataSize, int dataStart, int startPos, int loopPos, int bufferSize, int numBuffers, streamFlags_t flags ) = 0; virtual void CloseStreamedLoad( StreamHandle_t hStream ) = 0; virtual int CopyStreamedDataIntoMemory( StreamHandle_t hStream, void *pBuffer, int buffSize, int copyStartPos, int bytesToCopy ) = 0; virtual bool IsStreamedDataReady( StreamHandle_t hStream ) = 0; virtual void MarkBufferDiscarded( BufferHandle_t hBuffer ) = 0; virtual void *GetStreamedDataPointer( StreamHandle_t hStream, bool bSync ) = 0; virtual bool IsDataLoadInProgress( memhandle_t handle ) = 0; virtual void Flush() = 0; virtual void OnMixBegin() = 0; virtual void OnMixEnd() = 0; }; extern IAsyncWavDataCache *wavedatacache; struct CAudioSourceCachedInfoHandle_t { CAudioSourceCachedInfoHandle_t() : info( NULL ), m_FlushCount( 0 ) { } CAudioSourceCachedInfo *info; unsigned int m_FlushCount; inline CAudioSourceCachedInfo *Get( int audiosourcetype, bool soundisprecached, CSfxTable *sfx, int *pcacheddatasize ) { VPROF("CAudioSourceCachedInfoHandle_t::Get"); if ( m_FlushCount != s_nCurrentFlushCount ) { // Reacquire info = audiosourcecache->GetInfo( audiosourcetype, soundisprecached, sfx ); if ( pcacheddatasize ) { *pcacheddatasize = info ? info->CachedDataSize() : 0; } // Tag as current m_FlushCount = s_nCurrentFlushCount; } return info; } inline bool IsValid() { return !!( m_FlushCount == s_nCurrentFlushCount ); } inline CAudioSourceCachedInfo *FastGet() { VPROF("CAudioSourceCachedInfoHandle_t::FastGet"); if ( m_FlushCount != s_nCurrentFlushCount ) { return NULL; } return info; } static void InvalidateCache(); static unsigned int s_nCurrentFlushCount; }; //----------------------------------------------------------------------------- // Purpose: A source is an abstraction for a stream, cached file, or procedural // source of audio. //----------------------------------------------------------------------------- abstract_class CAudioSource { public: enum { AUDIO_SOURCE_UNK = 0, AUDIO_SOURCE_WAV, AUDIO_SOURCE_MP3, AUDIO_SOURCE_VOICE, AUDIO_SOURCE_MAXTYPE, }; enum { AUDIO_NOT_LOADED = 0, AUDIO_IS_LOADED = 1, AUDIO_LOADING = 2, }; virtual ~CAudioSource( void ) {} // Create an instance (mixer) of this audio source virtual CAudioMixer *CreateMixer( int initialStreamPosition = 0 ) = 0; // Serialization for caching virtual int GetType( void ) = 0; virtual void GetCacheData( CAudioSourceCachedInfo *info ) = 0; // Provide samples for the mixer. You can point pData at your own data, or if you prefer to copy the data, // you can copy it into copyBuf and set pData to copyBuf. virtual int GetOutputData( void **pData, int samplePosition, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] ) = 0; virtual int SampleRate( void ) = 0; // Returns true if the source is a voice source. // This affects the voice_overdrive behavior (all sounds get quieter when // someone is speaking). virtual bool IsVoiceSource() = 0; // Sample size is in bytes. It will not be accurate for compressed audio. This is a best estimate. // The compressed audio mixers understand this, but in general do not assume that SampleSize() * SampleCount() = filesize // or even that SampleSize() is 100% accurate due to compression. virtual int SampleSize( void ) = 0; // Total number of samples in this source. NOTE: Some sources are infinite (mic input), they should return // a count equal to one second of audio at their current rate. virtual int SampleCount( void ) = 0; virtual int Format( void ) = 0; virtual int DataSize( void ) = 0; virtual bool IsLooped( void ) = 0; virtual bool IsStereoWav( void ) = 0; virtual bool IsStreaming( void ) = 0; virtual int GetCacheStatus( void ) = 0; int IsCached( void ) { return GetCacheStatus() == AUDIO_IS_LOADED ? true : false; } virtual void CacheLoad( void ) = 0; virtual void CacheUnload( void ) = 0; virtual CSentence *GetSentence( void ) = 0; // these are used to find good splice/loop points. // If not implementing these, simply return sample virtual int ZeroCrossingBefore( int sample ) = 0; virtual int ZeroCrossingAfter( int sample ) = 0; // mixer's references virtual void ReferenceAdd( CAudioMixer *pMixer ) = 0; virtual void ReferenceRemove( CAudioMixer *pMixer ) = 0; // check reference count, return true if nothing is referencing this virtual bool CanDelete( void ) = 0; virtual void Prefetch() = 0; virtual bool IsAsyncLoad() = 0; // Make sure our data is rebuilt into the per-level cache virtual void CheckAudioSourceCache() = 0; virtual char const *GetFileName() = 0; virtual void SetPlayOnce( bool ) = 0; virtual bool IsPlayOnce() = 0; // Used to identify a word that is part of a sentence mixing operation virtual void SetSentenceWord( bool bIsWord ) = 0; virtual bool IsSentenceWord() = 0; virtual int SampleToStreamPosition( int samplePosition ) = 0; virtual int StreamToSamplePosition( int streamPosition ) = 0; }; // Fast method for determining duration of .wav/.mp3, exposed to server as well extern float AudioSource_GetSoundDuration( char const *pName ); // uses wave file cached in memory already extern float AudioSource_GetSoundDuration( CSfxTable *pSfx ); #endif // SND_AUDIO_SOURCE_H