//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: This abstracts the various hardware dependent implementations of sound // At the time of this writing there are Windows WAVEOUT, Direct Sound, // and Null implementations. // //=====================================================================================// #ifndef SOUNDSYSTEM_LOWLEVEL_H #define SOUNDSYSTEM_LOWLEVEL_H #pragma once #include "utlvector.h" #define SOUND_DEVICE_MAX_CHANNELS 8 // we support 2, 4, 6, & 8 channels currently. // Long term we may build 4 & 8 as matrix mix-downs of 6 channels #define MIX_BUFFER_SIZE 512 class IAudioDevice2; struct audio_device_init_params_t; class ALIGN16 CAudioMixBuffer { public: float m_flData[MIX_BUFFER_SIZE]; } ALIGN16_POST; const float MIX_DEFAULT_SAMPLING_RATE = 44100.0f; const float SECONDS_PER_SAMPLE = (1.0f / MIX_DEFAULT_SAMPLING_RATE); const float MIX_SECONDS_PER_BUFFER = float( MIX_BUFFER_SIZE ) / float( MIX_DEFAULT_SAMPLING_RATE ); const float MIX_BUFFERS_PER_SECOND = float( MIX_DEFAULT_SAMPLING_RATE ) / float( MIX_BUFFER_SIZE ); enum eSubSystems_t { AUDIO_SUBSYSTEM_XAUDIO = 0, AUDIO_SUBSYSTEM_DSOUND = 1, AUDIO_SUBSYSTEM_SDL = 2, AUDIO_SUBSYSTEM_NULL = 3, // fake, emulated device for failure cases }; #define AUDIO_DEVICE_NAME_MAX 256 struct audio_device_description_t { wchar_t m_deviceName[AUDIO_DEVICE_NAME_MAX]; char m_friendlyName[AUDIO_DEVICE_NAME_MAX]; uint8 m_nSubsystemId; uint8 m_nChannelCount; bool m_bIsDefault : 1; bool m_bIsAvailable : 1; audio_device_description_t() {} explicit audio_device_description_t( eSubSystems_t nSubsystem ) : m_nSubsystemId( (uint8)nSubsystem ) { Assert( nSubsystem >= 0 && nSubsystem <= UINT8_MAX ); } inline void InitAsNullDevice() { V_memset( m_deviceName, 0, sizeof(m_deviceName) ); V_memset( m_friendlyName, 0, sizeof(m_friendlyName) ); m_nChannelCount = 2; m_nSubsystemId = AUDIO_SUBSYSTEM_NULL; m_bIsDefault = true; m_bIsAvailable = true; } }; class CAudioDeviceList { public: eSubSystems_t m_nSubsystem; CUtlVector m_list; int m_nDefaultDevice; CAudioDeviceList() {} void BuildDeviceList( eSubSystems_t nPreferredSubsystem ); bool UpdateDeviceList(); // returns true if new devices or defaults show up audio_device_description_t *FindDeviceById( const char *pId ); // returns NULL if not found audio_device_description_t *GetDefaultDevice(); // returns NULL if not set bool IsValid() { return m_list.Count() > 0; } IAudioDevice2 *CreateDevice( audio_device_init_params_t ¶ms ); const wchar_t *GetDeviceToCreate( audio_device_init_params_t ¶ms ); private: uint m_nDeviceStamp; void UpdateDefaultDevice(); enum finddevice_t { FIND_ANY_DEVICE = 0, FIND_AVAILABLE_DEVICE_ONLY = 1, }; int FindDeviceById( const wchar_t *pId, finddevice_t nFind ); }; #define DEFAULT_MIX_BUFFER_COUNT 4 #define DEFAULT_MIX_BUFFER_SAMPLE_COUNT MIX_BUFFER_SIZE struct audio_device_init_params_t { const audio_device_description_t *m_pDesc; void *m_pWindowHandle; int m_nOutputBufferCount; int m_nSampleCountPerOutputBuffer; int m_nOverrideSpeakerConfig; // only used if m_bOverrideSpeakerConfig is true bool m_bOverrideDevice; // If this is set use m_overrideDevice bool m_bOverrideSpeakerConfig; bool m_bPlayEvenWhenNotInFocus; // When we set the override device it is important to copy the memory since // the original device description may get realloced and thus become a stale // pointer. wchar_t m_overrideDeviceName[AUDIO_DEVICE_NAME_MAX]; int m_nOverrideSubsystem; inline void OverrideDevice( audio_device_description_t *pDevice ) { m_bOverrideDevice = true; V_wcscpy_safe( m_overrideDeviceName, pDevice->m_deviceName ); m_nOverrideSubsystem = pDevice->m_nSubsystemId; } inline void OverrideSpeakerConfig( int nSpeakerConfig ) { Assert(nSpeakerConfig >= 0 && nSpeakerConfig < 8); m_nOverrideSpeakerConfig = nSpeakerConfig; m_bOverrideSpeakerConfig = true; } audio_device_init_params_t() : m_bOverrideSpeakerConfig(false), m_bOverrideDevice(false) {} inline void Defaults() { m_nOutputBufferCount = DEFAULT_MIX_BUFFER_COUNT; m_nSampleCountPerOutputBuffer = MIX_BUFFER_SIZE; m_bOverrideDevice = false; m_bOverrideSpeakerConfig = false; m_nOverrideSpeakerConfig = 0; m_bPlayEvenWhenNotInFocus = true; m_pWindowHandle = NULL; } }; extern int Audio_EnumerateDevices( eSubSystems_t nSubsystem, audio_device_description_t *pDeviceListOut, int nListCount ); extern int Audio_EnumerateXAudio2Devices( audio_device_description_t *pDeviceListOut, int nListCount ); extern int Audio_EnumerateDSoundDevices( audio_device_description_t *pDeviceListOut, int nListCount ); #ifdef POSIX extern int Audio_EnumerateSDLDevices( audio_device_description_t *pDeviceListOut, int nListCount ); #endif // return true if there was an error event and the device needs to be restarted extern bool Audio_PollErrorEvents(); class IAudioDevice2 { public: virtual ~IAudioDevice2() {} virtual void OutputBuffer( int nChannels, CAudioMixBuffer *pChannelArray ) = 0; virtual void Shutdown( void ) = 0; virtual int QueuedBufferCount() = 0; virtual int EmptyBufferCount() = 0; virtual void CancelOutput( void ) = 0; virtual void WaitForComplete() = 0; virtual void UpdateFocus( bool bWindowHasFocus ) = 0; virtual void ClearBuffer() = 0; virtual const wchar_t *GetDeviceID() const = 0; virtual void OutputDebugInfo() const = 0; virtual bool SetShouldPlayWhenNotInFocus( bool bPlayEvenWhenNotInFocus ) = 0; inline const char *Name() const { return m_pName; } inline int ChannelCount() const { return m_nChannels; } inline int MixChannelCount() const { return m_nChannels > 6 ? 6 : m_nChannels; } // 7.1 mixes as 5.1 inline int BitsPerSample() const { return m_nSampleBits; } inline int SampleRate() const { return m_nSampleRate; } inline bool IsSurround() const { return m_nChannels > 2 ? true : false; } inline bool IsSurroundCenter() const { return m_nChannels > 4 ? true : false; } inline bool IsActive() const { return m_bIsActive; } inline bool IsHeadphone() const { return m_bIsHeadphone; } // mixing makes some choices differently for stereo vs headphones, expose that here. inline bool CanDetectBufferStarvation() { return m_bSupportsBufferStarvationDetection; } inline bool IsCaptureDevice() { return m_bIsCaptureDevice; } inline int DeviceSampleBytes( void ) const { return BitsPerSample() / 8; } // UNDONE: Need to implement these void Pause() {} void UnPause() {} void TransferSamples( uint32 nEndTimeIgnored ); protected: // NOTE: Derived classes MUST initialize these before returning a device from a factory const char *m_pName; int m_nChannels; int m_nSampleBits; int m_nSampleRate; bool m_bIsActive; bool m_bIsHeadphone; bool m_bSupportsBufferStarvationDetection; bool m_bIsCaptureDevice; }; // device handling extern IAudioDevice2 *Audio_CreateXAudio2Device( const audio_device_init_params_t ¶ms ); extern IAudioDevice2 *Audio_CreateDSoundDevice( const audio_device_init_params_t ¶ms ); #ifdef POSIX extern IAudioDevice2 *Audio_CreateSDLDevice( const audio_device_init_params_t ¶ms ); #endif extern IAudioDevice2 *Audio_CreateNullDevice(); #if IS_WINDOWS_PC extern bool GetWindowsDefaultAudioDevice( wchar_t *pName, size_t nNameBufSize ); #endif // speaker config extern int SpeakerConfigValueToChannelCount( int nSpeakerConfig ); extern int ChannelCountToSpeakerConfigValue( int nChannelCount, bool bIsHeadphone ); // buffer library extern void ScaleBuffer( float flOutput[MIX_BUFFER_SIZE], const float flInput[MIX_BUFFER_SIZE], float flScale ); extern void ScaleBufferRamp( float flOutput[MIX_BUFFER_SIZE], const float flInput[MIX_BUFFER_SIZE], float flScaleStart, float flScaleEnd ); extern void MixBuffer( float flOutput[MIX_BUFFER_SIZE], const float flInput[MIX_BUFFER_SIZE], float flScale ); extern void MixBufferRamp( float flOutput[MIX_BUFFER_SIZE], const float flInput[MIX_BUFFER_SIZE], float flScaleStart, float flScaleEnd ); inline void ScaleBufferAuto( float flOutput[MIX_BUFFER_SIZE], const float flInput[MIX_BUFFER_SIZE], float flScaleStart, float flScaleEnd ) { if ( flScaleStart == flScaleEnd ) { ScaleBuffer( flOutput, flInput, flScaleEnd ); } else { ScaleBufferRamp( flOutput, flInput, flScaleStart, flScaleEnd ); } } inline void MixBufferAuto( float flOutput[MIX_BUFFER_SIZE], const float flInput[MIX_BUFFER_SIZE], float flScaleStart, float flScaleEnd ) { if ( flScaleStart == flScaleEnd ) { MixBuffer( flOutput, flInput, flScaleEnd ); } else { MixBufferRamp( flOutput, flInput, flScaleStart, flScaleEnd ); } } extern void SilenceBuffer( float flBuffer[MIX_BUFFER_SIZE] ); extern void SilenceBuffers( CAudioMixBuffer *pBuffers, int nBufferCount ); extern void SumBuffer2x1( float flOutput[MIX_BUFFER_SIZE], float flInput0[MIX_BUFFER_SIZE], float flScale0, float flInput1[MIX_BUFFER_SIZE], float flScale1 ); extern void SwapBuffersInPlace( float flInput0[MIX_BUFFER_SIZE], float flInput1[MIX_BUFFER_SIZE] ); extern float BufferLevel( float flInput[MIX_BUFFER_SIZE] ); extern float AvergeBufferAmplitude( float flInput[MIX_BUFFER_SIZE] ); extern void ConvertFloat32Int16_Clamp_Interleave2( short *pOut, float *pflLeft, float *pflRight, int nSampleCount ); extern void ConvertFloat32Int16_Clamp_InterleaveStride( short *pOut, int nOutputChannelCount, int nChannelStrideFloats, float *pflChannel0, int nInputChannelCount, int nSampleCount ); #if IS_WINDOWS_PC void InitCOM(); void ShutdownCOM(); #else inline void InitCOM() {} inline void ShutdownCOM() {} #endif #endif // SOUNDSYSTEM_LOWLEVEL_H