You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
9.6 KiB
263 lines
9.6 KiB
//========= 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<audio_device_description_t> 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
|