source-engine/engine/audio/snd_dev_openal.cpp

612 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#include "audio_pch.h"
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#ifdef OSX
#include <OpenAL/MacOSX_OALExtensions.h>
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#ifndef DEDICATED // have to test this because VPC is forcing us to compile this file.
extern bool snd_firsttime;
extern bool MIX_ScaleChannelVolume( paintbuffer_t *ppaint, channel_t *pChannel, int volume[CCHANVOLUMES], int mixchans );
extern void S_SpatializeChannel( int volume[6], int master_vol, const Vector *psourceDir, float gain, float mono );
#define NUM_BUFFERS_SOURCES 128
#define BUFF_MASK (NUM_BUFFERS_SOURCES - 1 )
#define BUFFER_SIZE 0x0400
//-----------------------------------------------------------------------------
//
// NOTE: This only allows 16-bit, stereo wave out
//
//-----------------------------------------------------------------------------
class CAudioDeviceOpenAL : public CAudioDeviceBase
{
public:
bool IsActive( void );
bool Init( void );
void Shutdown( void );
void PaintEnd( void );
int GetOutputPosition( void );
void ChannelReset( int entnum, int channelIndex, float distanceMod );
void Pause( void );
void UnPause( void );
float MixDryVolume( void );
bool Should3DMix( void );
void StopAllSounds( void );
int PaintBegin( float mixAheadTime, int soundtime, int paintedtime );
void ClearBuffer( void );
void UpdateListener( const Vector& position, const Vector& forward, const Vector& right, const Vector& up );
void MixBegin( int sampleCount );
void MixUpsample( int sampleCount, int filtertype );
void Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress );
void Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress );
void Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress );
void Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress );
void TransferSamples( int end );
void SpatializeChannel( int volume[CCHANVOLUMES/2], int master_vol, const Vector& sourceDir, float gain, float mono);
void ApplyDSPEffects( int idsp, portable_samplepair_t *pbuffront, portable_samplepair_t *pbufrear, portable_samplepair_t *pbufcenter, int samplecount );
const char *DeviceName( void ) { return "OpenAL"; }
int DeviceChannels( void ) { return 2; }
int DeviceSampleBits( void ) { return 16; }
int DeviceSampleBytes( void ) { return 2; }
int DeviceDmaSpeed( void ) { return SOUND_DMA_SPEED; }
int DeviceSampleCount( void ) { return m_deviceSampleCount; }
private:
void OpenWaveOut( void );
void CloseWaveOut( void );
bool ValidWaveOut( void ) const;
ALuint m_Buffer[NUM_BUFFERS_SOURCES];
ALuint m_Source[1];
int m_SndBufSize;
void *m_sndBuffers;
int m_deviceSampleCount;
int m_buffersSent;
int m_buffersCompleted;
int m_pauseCount;
bool m_bSoundsShutdown;
};
IAudioDevice *Audio_CreateOpenALDevice( void )
{
CAudioDeviceOpenAL *wave = NULL;
if ( !wave )
{
wave = new CAudioDeviceOpenAL;
}
if ( wave->Init() )
return wave;
delete wave;
wave = NULL;
return NULL;
}
void OnSndSurroundCvarChanged( IConVar *pVar, const char *pOldString, float flOldValue );
void OnSndSurroundLegacyChanged( IConVar *pVar, const char *pOldString, float flOldValue );
//-----------------------------------------------------------------------------
// Init, shutdown
//-----------------------------------------------------------------------------
bool CAudioDeviceOpenAL::Init( void )
{
m_SndBufSize = 0;
m_sndBuffers = NULL;
m_pauseCount = 0;
m_bSurround = false;
m_bSurroundCenter = false;
m_bHeadphone = false;
m_buffersSent = 0;
m_buffersCompleted = 0;
m_pauseCount = 0;
m_bSoundsShutdown = false;
static bool first = true;
if ( first )
{
snd_surround.SetValue( 2 );
snd_surround.InstallChangeCallback( &OnSndSurroundCvarChanged );
snd_legacy_surround.InstallChangeCallback( &OnSndSurroundLegacyChanged );
first = false;
}
OpenWaveOut();
if ( snd_firsttime )
{
DevMsg( "Wave sound initialized\n" );
}
return ValidWaveOut();
}
void CAudioDeviceOpenAL::Shutdown( void )
{
CloseWaveOut();
}
//-----------------------------------------------------------------------------
// WAV out device
//-----------------------------------------------------------------------------
inline bool CAudioDeviceOpenAL::ValidWaveOut( void ) const
{
return m_sndBuffers != 0;
}
//-----------------------------------------------------------------------------
// Opens the windows wave out device
//-----------------------------------------------------------------------------
void CAudioDeviceOpenAL::OpenWaveOut( void )
{
m_buffersSent = 0;
m_buffersCompleted = 0;
ALenum error;
ALCcontext *newContext = NULL;
ALCdevice *newDevice = NULL;
// Create a new OpenAL Device
// Pass NULL to specify the systemuse default output device
const ALCchar *initStr = (const ALCchar *)"\'( (sampling-rate 44100 ))";
newDevice = alcOpenDevice(initStr);
if (newDevice != NULL)
{
// Create a new OpenAL Context
// The new context will render to the OpenAL Device just created
ALCint attr[] = { ALC_FREQUENCY, DeviceDmaSpeed(), ALC_SYNC, AL_FALSE, 0 };
newContext = alcCreateContext(newDevice, attr );
if (newContext != NULL)
{
// Make the new context the Current OpenAL Context
alcMakeContextCurrent(newContext);
// Create some OpenAL Buffer Objects
alGenBuffers( NUM_BUFFERS_SOURCES, m_Buffer);
if((error = alGetError()) != AL_NO_ERROR)
{
DevMsg("Error Generating Buffers: ");
return;
}
// Create some OpenAL Source Objects
alGenSources(1, m_Source);
if(alGetError() != AL_NO_ERROR)
{
DevMsg("Error generating sources! \n");
return;
}
alListener3f( AL_POSITION,0.0f,0.0f,0.0f);
int i;
for ( i = 0; i < 1; i++ )
{
alSource3f( m_Source[i],AL_POSITION,0.0f,0.0f,0.0f );
alSourcef( m_Source[i], AL_PITCH, 1.0f );
alSourcef( m_Source[i], AL_GAIN, 1.0f );
}
}
}
m_SndBufSize = NUM_BUFFERS_SOURCES*BUFFER_SIZE;
m_deviceSampleCount = m_SndBufSize / DeviceSampleBytes();
if ( !m_sndBuffers )
{
m_sndBuffers = malloc( m_SndBufSize );
memset( m_sndBuffers, 0x0, m_SndBufSize );
}
}
//-----------------------------------------------------------------------------
// Closes the windows wave out device
//-----------------------------------------------------------------------------
void CAudioDeviceOpenAL::CloseWaveOut( void )
{
if ( ValidWaveOut() )
{
ALCcontext *context = NULL;
ALCdevice *device = NULL;
m_bSoundsShutdown = true;
alSourceStop( m_Source[0] );
// Delete the Sources
alDeleteSources(1, m_Source);
// Delete the Buffers
alDeleteBuffers(NUM_BUFFERS_SOURCES, m_Buffer);
//Get active context
context = alcGetCurrentContext();
//Get device for active context
device = alcGetContextsDevice(context);
alcMakeContextCurrent( NULL );
alcSuspendContext(context);
//Release context
alcDestroyContext(context);
//Close device
alcCloseDevice(device);
}
if ( m_sndBuffers )
{
free( m_sndBuffers );
m_sndBuffers = NULL;
}
}
//-----------------------------------------------------------------------------
// Mixing setup
//-----------------------------------------------------------------------------
int CAudioDeviceOpenAL::PaintBegin( float mixAheadTime, int soundtime, int paintedtime )
{
// soundtime - total samples that have been played out to hardware at dmaspeed
// paintedtime - total samples that have been mixed at speed
// endtime - target for samples in mixahead buffer at speed
unsigned int endtime = soundtime + mixAheadTime * DeviceDmaSpeed();
int samps = DeviceSampleCount() >> (DeviceChannels()-1);
if ((int)(endtime - soundtime) > samps)
endtime = soundtime + samps;
if ((endtime - paintedtime) & 0x3)
{
// The difference between endtime and painted time should align on
// boundaries of 4 samples. This is important when upsampling from 11khz -> 44khz.
endtime -= (endtime - paintedtime) & 0x3;
}
return endtime;
}
#ifdef OSX
ALvoid alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq)
{
static alBufferDataStaticProcPtr proc = NULL;
if (proc == NULL) {
proc = (alBufferDataStaticProcPtr) alGetProcAddress((const ALCchar*) "alBufferDataStatic");
}
if (proc)
proc(bid, format, data, size, freq);
}
#endif
//-----------------------------------------------------------------------------
// Actually performs the mixing
//-----------------------------------------------------------------------------
void CAudioDeviceOpenAL::PaintEnd( void )
{
if ( !m_sndBuffers /*|| m_bSoundsShutdown*/ )
return;
ALint state;
ALenum error;
int iloop;
int cblocks = 4 << 1;
ALint processed = 1;
ALuint lastUnqueuedBuffer = 0;
ALuint unqueuedBuffer = -1;
int nProcessedLoop = 200; // spin for a max of 200 times de-queing buffers, fixes a hang on exit
while ( processed > 0 && --nProcessedLoop > 0 )
{
alGetSourcei( m_Source[ 0 ], AL_BUFFERS_PROCESSED, &processed);
error = alGetError();
if (error != AL_NO_ERROR)
break;
if ( processed > 0 )
{
lastUnqueuedBuffer = unqueuedBuffer;
alSourceUnqueueBuffers( m_Source[ 0 ], 1, &unqueuedBuffer );
error = alGetError();
if ( error != AL_NO_ERROR && error != AL_INVALID_NAME )
{
DevMsg( "Error alSourceUnqueueBuffers %d\n", error );
break;
}
else
{
m_buffersCompleted++; // this buffer has been played
}
}
}
//
// submit a few new sound blocks
//
// 44K sound support
while (((m_buffersSent - m_buffersCompleted) >> SAMPLE_16BIT_SHIFT) < cblocks)
{
int iBuf = m_buffersSent&BUFF_MASK;
#ifdef OSX
alBufferDataStaticProc( m_Buffer[iBuf], AL_FORMAT_STEREO16, (char *)m_sndBuffers + iBuf*BUFFER_SIZE, BUFFER_SIZE, DeviceDmaSpeed() );
#else
alBufferData( m_Buffer[iBuf], AL_FORMAT_STEREO16, (char *)m_sndBuffers + iBuf*BUFFER_SIZE, BUFFER_SIZE, DeviceDmaSpeed() );
#endif
if ( (error = alGetError()) != AL_NO_ERROR )
{
DevMsg( "Error alBufferData %d %d\n", iBuf, error );
}
alSourceQueueBuffers( m_Source[0], 1, &m_Buffer[iBuf] );
if ( (error = alGetError() ) != AL_NO_ERROR )
{
DevMsg( "Error alSourceQueueBuffers %d %d\n", iBuf, error );
}
m_buffersSent++;
}
// make sure the stream is playing
alGetSourcei( m_Source[ 0 ], AL_SOURCE_STATE, &state);
if ( state != AL_PLAYING )
{
DevMsg( "Restarting sound playback\n" );
alSourcePlay( m_Source[0] );
if((error = alGetError()) != AL_NO_ERROR)
{
DevMsg( "Error alSourcePlay %d\n", error );
}
}
}
int CAudioDeviceOpenAL::GetOutputPosition( void )
{
int s = m_buffersSent * BUFFER_SIZE;
s >>= SAMPLE_16BIT_SHIFT;
s &= (DeviceSampleCount()-1);
return s / DeviceChannels();
}
//-----------------------------------------------------------------------------
// Pausing
//-----------------------------------------------------------------------------
void CAudioDeviceOpenAL::Pause( void )
{
m_pauseCount++;
if (m_pauseCount == 1)
{
alSourceStop( m_Source[0] );
}
}
void CAudioDeviceOpenAL::UnPause( void )
{
if ( m_pauseCount > 0 )
{
m_pauseCount--;
}
if ( m_pauseCount == 0 )
{
alSourcePlay( m_Source[0] );
}
}
bool CAudioDeviceOpenAL::IsActive( void )
{
return ( m_pauseCount == 0 );
}
float CAudioDeviceOpenAL::MixDryVolume( void )
{
return 0;
}
bool CAudioDeviceOpenAL::Should3DMix( void )
{
return false;
}
void CAudioDeviceOpenAL::ClearBuffer( void )
{
if ( !m_sndBuffers )
return;
Q_memset( m_sndBuffers, 0x0, DeviceSampleCount() * DeviceSampleBytes() );
}
void CAudioDeviceOpenAL::UpdateListener( const Vector& position, const Vector& forward, const Vector& right, const Vector& up )
{
}
void CAudioDeviceOpenAL::MixBegin( int sampleCount )
{
MIX_ClearAllPaintBuffers( sampleCount, false );
}
void CAudioDeviceOpenAL::MixUpsample( int sampleCount, int filtertype )
{
paintbuffer_t *ppaint = MIX_GetCurrentPaintbufferPtr();
int ifilter = ppaint->ifilter;
Assert (ifilter < CPAINTFILTERS);
S_MixBufferUpsample2x( sampleCount, ppaint->pbuf, &(ppaint->fltmem[ifilter][0]), CPAINTFILTERMEM, filtertype );
ppaint->ifilter++;
}
void CAudioDeviceOpenAL::Mix8Mono( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress )
{
int volume[CCHANVOLUMES];
paintbuffer_t *ppaint = MIX_GetCurrentPaintbufferPtr();
if (!MIX_ScaleChannelVolume( ppaint, pChannel, volume, 1))
return;
Mix8MonoWavtype( pChannel, ppaint->pbuf + outputOffset, volume, (byte *)pData, inputOffset, rateScaleFix, outCount );
}
void CAudioDeviceOpenAL::Mix8Stereo( channel_t *pChannel, char *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress )
{
int volume[CCHANVOLUMES];
paintbuffer_t *ppaint = MIX_GetCurrentPaintbufferPtr();
if (!MIX_ScaleChannelVolume( ppaint, pChannel, volume, 2 ))
return;
Mix8StereoWavtype( pChannel, ppaint->pbuf + outputOffset, volume, (byte *)pData, inputOffset, rateScaleFix, outCount );
}
void CAudioDeviceOpenAL::Mix16Mono( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress )
{
int volume[CCHANVOLUMES];
paintbuffer_t *ppaint = MIX_GetCurrentPaintbufferPtr();
if (!MIX_ScaleChannelVolume( ppaint, pChannel, volume, 1 ))
return;
Mix16MonoWavtype( pChannel, ppaint->pbuf + outputOffset, volume, pData, inputOffset, rateScaleFix, outCount );
}
void CAudioDeviceOpenAL::Mix16Stereo( channel_t *pChannel, short *pData, int outputOffset, int inputOffset, fixedint rateScaleFix, int outCount, int timecompress )
{
int volume[CCHANVOLUMES];
paintbuffer_t *ppaint = MIX_GetCurrentPaintbufferPtr();
if (!MIX_ScaleChannelVolume( ppaint, pChannel, volume, 2 ))
return;
Mix16StereoWavtype( pChannel, ppaint->pbuf + outputOffset, volume, pData, inputOffset, rateScaleFix, outCount );
}
void CAudioDeviceOpenAL::ChannelReset( int entnum, int channelIndex, float distanceMod )
{
}
void CAudioDeviceOpenAL::TransferSamples( int end )
{
int lpaintedtime = g_paintedtime;
int endtime = end;
// resumes playback...
if ( m_sndBuffers )
{
S_TransferStereo16( m_sndBuffers, PAINTBUFFER, lpaintedtime, endtime );
}
}
void CAudioDeviceOpenAL::SpatializeChannel( int volume[CCHANVOLUMES/2], int master_vol, const Vector& sourceDir, float gain, float mono )
{
VPROF("CAudioDeviceOpenAL::SpatializeChannel");
S_SpatializeChannel( volume, master_vol, &sourceDir, gain, mono );
}
void CAudioDeviceOpenAL::StopAllSounds( void )
{
m_bSoundsShutdown = true;
alSourceStop( m_Source[0] );
}
void CAudioDeviceOpenAL::ApplyDSPEffects( int idsp, portable_samplepair_t *pbuffront, portable_samplepair_t *pbufrear, portable_samplepair_t *pbufcenter, int samplecount )
{
//SX_RoomFX( endtime, filter, timefx );
DSP_Process( idsp, pbuffront, pbufrear, pbufcenter, samplecount );
}
static uint32 GetOSXSpeakerConfig()
{
return 2;
}
static uint32 GetSpeakerConfigForSurroundMode( int surroundMode, const char **pConfigDesc )
{
uint32 newSpeakerConfig = 2;
*pConfigDesc = "stereo speaker";
return newSpeakerConfig;
}
void OnSndSurroundCvarChanged( IConVar *pVar, const char *pOldString, float flOldValue )
{
// if the old value is -1, we're setting this from the detect routine for the first time
// no need to reset the device
if ( flOldValue == -1 )
return;
// get the user's previous speaker config
uint32 speaker_config = GetOSXSpeakerConfig();
// get the new config
uint32 newSpeakerConfig = 0;
const char *speakerConfigDesc = "";
ConVarRef var( pVar );
newSpeakerConfig = GetSpeakerConfigForSurroundMode( var.GetInt(), &speakerConfigDesc );
// make sure the config has changed
if (newSpeakerConfig == speaker_config)
return;
// set new configuration
//SetWindowsSpeakerConfig(newSpeakerConfig);
Msg("Speaker configuration has been changed to %s.\n", speakerConfigDesc);
// restart sound system so it takes effect
//g_pSoundServices->RestartSoundSystem();
}
void OnSndSurroundLegacyChanged( IConVar *pVar, const char *pOldString, float flOldValue )
{
}
#endif // !DEDICATED