diff options
Diffstat (limited to 'engine/audio/private/snd_dev_sdl.cpp')
| -rw-r--r-- | engine/audio/private/snd_dev_sdl.cpp | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/engine/audio/private/snd_dev_sdl.cpp b/engine/audio/private/snd_dev_sdl.cpp new file mode 100644 index 0000000..642090f --- /dev/null +++ b/engine/audio/private/snd_dev_sdl.cpp @@ -0,0 +1,574 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "audio_pch.h" + +#if !DEDICATED + +#include "tier0/dynfunction.h" +#include "video//ivideoservices.h" +#include "../../sys_dll.h" + +// prevent some conflicts in SDL headers... +#undef M_PI +#include <stdint.h> +#ifndef _STDINT_H_ +#define _STDINT_H_ 1 +#endif + +#include "SDL.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern bool snd_firsttime; +extern bool MIX_ScaleChannelVolume( paintbuffer_t *ppaint, channel_t *pChannel, int volume[CCHANVOLUMES], int mixchans ); +extern void S_SpatializeChannel( /*int nSlot,*/ int volume[6], int master_vol, const Vector *psourceDir, float gain, float mono ); + +// 64K is about 1/3 second at 16-bit, stereo, 44100 Hz +// 44k: UNDONE - need to double buffers now that we're playing back at 44100? +#define WAV_BUFFERS 64 +#define WAV_MASK (WAV_BUFFERS - 1) +#define WAV_BUFFER_SIZE 0x0400 + +#if 0 +#define debugsdl printf +#else +static inline void debugsdl(const char *fmt, ...) {} +#endif + + +//----------------------------------------------------------------------------- +// +// NOTE: This only allows 16-bit, stereo wave out (!!! FIXME: but SDL supports 7.1, etc, too!) +// +//----------------------------------------------------------------------------- +class CAudioDeviceSDLAudio : public CAudioDeviceBase +{ +public: + CAudioDeviceSDLAudio(); + virtual ~CAudioDeviceSDLAudio(); + + 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 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 nSlot, 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 "SDL"; } + 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: + SDL_AudioDeviceID m_devId; + + static void SDLCALL AudioCallbackEntry(void *userdata, Uint8 * stream, int len); + void AudioCallback(Uint8 *stream, int len); + + void OpenWaveOut( void ); + void CloseWaveOut( void ); + void AllocateOutputBuffers(); + void FreeOutputBuffers(); + bool ValidWaveOut( void ) const; + + int m_deviceSampleCount; + + int m_buffersSent; + int m_pauseCount; + int m_readPos; + int m_partialWrite; + + // Memory for the wave data + uint8_t *m_pBuffer; +}; + +static CAudioDeviceSDLAudio *g_wave = NULL; + +//----------------------------------------------------------------------------- +// Constructor (just lookup SDL entry points, real work happens in this->Init()) +//----------------------------------------------------------------------------- +CAudioDeviceSDLAudio::CAudioDeviceSDLAudio() +{ + m_devId = 0; +} + +//----------------------------------------------------------------------------- +// Destructor. Make sure our global pointer gets set to NULL. +//----------------------------------------------------------------------------- +CAudioDeviceSDLAudio::~CAudioDeviceSDLAudio() +{ + g_wave = NULL; +} + +//----------------------------------------------------------------------------- +// Class factory +//----------------------------------------------------------------------------- +IAudioDevice *Audio_CreateSDLAudioDevice( void ) +{ + if ( !g_wave ) + { + g_wave = new CAudioDeviceSDLAudio; + Assert( g_wave ); + } + + if ( g_wave && !g_wave->Init() ) + { + delete g_wave; + g_wave = NULL; + } + + return g_wave; +} + + +//----------------------------------------------------------------------------- +// Init, shutdown +//----------------------------------------------------------------------------- +bool CAudioDeviceSDLAudio::Init( void ) +{ + // If we've already got a device open, then return. This allows folks to call + // Audio_CreateSDLAudioDevice() multiple times. CloseWaveOut() will free the + // device, and set m_devId to 0. + if( m_devId ) + return true; + + m_bSurround = false; + m_bSurroundCenter = false; + m_bHeadphone = false; + m_buffersSent = 0; + m_pauseCount = 0; + m_pBuffer = NULL; + m_readPos = 0; + m_partialWrite = 0; + m_devId = 0; + + OpenWaveOut(); + + if ( snd_firsttime ) + { + DevMsg( "Wave sound initialized\n" ); + } + + return ValidWaveOut(); +} + +void CAudioDeviceSDLAudio::Shutdown( void ) +{ + CloseWaveOut(); +} + + +//----------------------------------------------------------------------------- +// WAV out device +//----------------------------------------------------------------------------- +inline bool CAudioDeviceSDLAudio::ValidWaveOut( void ) const +{ + return m_devId != 0; +} + + +//----------------------------------------------------------------------------- +// Opens the windows wave out device +//----------------------------------------------------------------------------- +void CAudioDeviceSDLAudio::OpenWaveOut( void ) +{ + debugsdl("SDLAUDIO: OpenWaveOut...\n"); + +#ifndef WIN32 + char appname[ 256 ]; + KeyValues *modinfo = new KeyValues( "ModInfo" ); + + if ( modinfo->LoadFromFile( g_pFileSystem, "gameinfo.txt" ) ) + Q_strncpy( appname, modinfo->GetString( "game" ), sizeof( appname ) ); + else + Q_strncpy( appname, "Source1 Game", sizeof( appname ) ); + + modinfo->deleteThis(); + modinfo = NULL; + + // Set these environment variables, in case we're using PulseAudio. + setenv("PULSE_PROP_application.name", appname, 1); + setenv("PULSE_PROP_media.role", "game", 1); +#endif + + // !!! FIXME: specify channel map, etc + // !!! FIXME: set properties (role, icon, etc). + + //#define SDLAUDIO_FAIL(fnstr) do { DevWarning(fnstr " failed"); CloseWaveOut(); return; } while (false) + //#define SDLAUDIO_FAIL(fnstr) do { printf("SDLAUDIO: " fnstr " failed: %s\n", SDL_GetError ? SDL_GetError() : "???"); CloseWaveOut(); return; } while (false) + #define SDLAUDIO_FAIL(fnstr) do { const char *err = SDL_GetError(); printf("SDLAUDIO: " fnstr " failed: %s\n", err ? err : "???"); CloseWaveOut(); return; } while (false) + + if (!SDL_WasInit(SDL_INIT_AUDIO)) + { + if (SDL_InitSubSystem(SDL_INIT_AUDIO)) + SDLAUDIO_FAIL("SDL_InitSubSystem(SDL_INIT_AUDIO)"); + } + + debugsdl("SDLAUDIO: Using SDL audio target '%s'\n", SDL_GetCurrentAudioDriver()); + + // Open an audio device... + // !!! FIXME: let user specify a device? + // !!! FIXME: we can handle quad, 5.1, 7.1, etc here. + SDL_AudioSpec desired, obtained; + memset(&desired, '\0', sizeof (desired)); + desired.freq = SOUND_DMA_SPEED; + desired.format = AUDIO_S16SYS; + desired.channels = 2; + desired.samples = 2048; + desired.callback = &CAudioDeviceSDLAudio::AudioCallbackEntry; + desired.userdata = this; + m_devId = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, SDL_AUDIO_ALLOW_ANY_CHANGE); + + if (!m_devId) + SDLAUDIO_FAIL("SDL_OpenAudioDevice()"); + + #undef SDLAUDIO_FAIL + + // We're now ready to feed audio data to SDL! + AllocateOutputBuffers(); + SDL_PauseAudioDevice(m_devId, 0); + +#if defined( BINK_VIDEO ) && defined( LINUX ) + // Tells Bink to use SDL for its audio decoding + if ( g_pVideo != NULL) + { + g_pVideo->SoundDeviceCommand( VideoSoundDeviceOperation::SET_SDL_PARAMS, NULL, (void *)&obtained ); + + } +#endif +} + +//----------------------------------------------------------------------------- +// Closes the windows wave out device +//----------------------------------------------------------------------------- +void CAudioDeviceSDLAudio::CloseWaveOut( void ) +{ + // none of these SDL_* functions are available to call if this is false. + if (m_devId) + { + SDL_CloseAudioDevice(m_devId); + m_devId = 0; + } + SDL_QuitSubSystem(SDL_INIT_AUDIO); + FreeOutputBuffers(); +} + +//----------------------------------------------------------------------------- +// Allocate output buffers +//----------------------------------------------------------------------------- +void CAudioDeviceSDLAudio::AllocateOutputBuffers() +{ + // Allocate and lock memory for the waveform data. + const int nBufferSize = WAV_BUFFER_SIZE * WAV_BUFFERS; + m_pBuffer = new uint8_t[nBufferSize]; + memset(m_pBuffer, '\0', nBufferSize); + m_readPos = 0; + m_partialWrite = 0; + m_deviceSampleCount = nBufferSize / DeviceSampleBytes(); +} + + +//----------------------------------------------------------------------------- +// Free output buffers +//----------------------------------------------------------------------------- +void CAudioDeviceSDLAudio::FreeOutputBuffers() +{ + delete[] m_pBuffer; + m_pBuffer = NULL; +} + + +//----------------------------------------------------------------------------- +// Mixing setup +//----------------------------------------------------------------------------- +int CAudioDeviceSDLAudio::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; +} + +void CAudioDeviceSDLAudio::AudioCallbackEntry(void *userdata, Uint8 *stream, int len) +{ + ((CAudioDeviceSDLAudio *) userdata)->AudioCallback(stream, len); +} + +void CAudioDeviceSDLAudio::AudioCallback(Uint8 *stream, int len) +{ + if (!m_devId) + { + debugsdl("SDLAUDIO: uhoh, no audio device!\n"); + return; // can this even happen? + } + + const int totalWriteable = len; +#if defined( BINK_VIDEO ) && defined( LINUX ) + Uint8 *stream_orig = stream; +#endif + debugsdl("SDLAUDIO: writable size is %d.\n", totalWriteable); + + Assert(len <= (WAV_BUFFERS * WAV_BUFFER_SIZE)); + + while (len > 0) + { + // spaceAvailable == bytes before we overrun the end of the ring buffer. + const int spaceAvailable = ((WAV_BUFFERS * WAV_BUFFER_SIZE) - m_readPos); + const int writeLen = (len < spaceAvailable) ? len : spaceAvailable; + + if (writeLen > 0) + { + const uint8_t *buf = m_pBuffer + m_readPos; + debugsdl("SDLAUDIO: Writing %d bytes...\n", writeLen); + + #if 0 + static FILE *io = NULL; + if (io == NULL) io = fopen("dumpplayback.raw", "wb"); + if (io != NULL) { fwrite(buf, writeLen, 1, io); fflush(io); } + #endif + + memcpy(stream, buf, writeLen); + stream += writeLen; + len -= writeLen; + Assert(len >= 0); + } + + m_readPos = len ? 0 : (m_readPos + writeLen); // if still bytes to write to stream, we're rolling around the ring buffer. + } + +#if defined( BINK_VIDEO ) && defined( LINUX ) + // Mix in Bink movie audio if that stuff is playing. + if ( g_pVideo != NULL) + { + g_pVideo->SoundDeviceCommand( VideoSoundDeviceOperation::SDLMIXER_CALLBACK, (void *)stream_orig, (void *)&totalWriteable ); + } +#endif + + // Translate between bytes written and buffers written. + m_partialWrite += totalWriteable; + m_buffersSent += m_partialWrite / WAV_BUFFER_SIZE; + m_partialWrite %= WAV_BUFFER_SIZE; +} + + +//----------------------------------------------------------------------------- +// Actually performs the mixing +//----------------------------------------------------------------------------- +void CAudioDeviceSDLAudio::PaintEnd( void ) +{ + debugsdl("SDLAUDIO: PaintEnd...\n"); + +#if 0 // !!! FIXME: this is the 1.3 headers, but not implemented yet in SDL. + if (SDL_AudioDeviceConnected(m_devId) != 1) + { + debugsdl("SDLAUDIO: Audio device was disconnected!\n"); + Shutdown(); + } +#endif +} + +int CAudioDeviceSDLAudio::GetOutputPosition( void ) +{ + return (m_readPos >> SAMPLE_16BIT_SHIFT)/DeviceChannels(); +} + + +//----------------------------------------------------------------------------- +// Pausing +//----------------------------------------------------------------------------- +void CAudioDeviceSDLAudio::Pause( void ) +{ + m_pauseCount++; + if (m_pauseCount == 1) + { + debugsdl("SDLAUDIO: PAUSE\n"); + SDL_PauseAudioDevice(m_devId, 1); + } +} + + +void CAudioDeviceSDLAudio::UnPause( void ) +{ + if ( m_pauseCount > 0 ) + { + m_pauseCount--; + if (m_pauseCount == 0) + { + debugsdl("SDLAUDIO: UNPAUSE\n"); + SDL_PauseAudioDevice(m_devId, 0); + } + } +} + +bool CAudioDeviceSDLAudio::IsActive( void ) +{ + return ( m_pauseCount == 0 ); +} + +float CAudioDeviceSDLAudio::MixDryVolume( void ) +{ + return 0; +} + + +bool CAudioDeviceSDLAudio::Should3DMix( void ) +{ + return false; +} + + +void CAudioDeviceSDLAudio::ClearBuffer( void ) +{ + int clear; + + if ( !m_pBuffer ) + return; + + clear = 0; + + Q_memset(m_pBuffer, clear, DeviceSampleCount() * DeviceSampleBytes() ); +} + + +void CAudioDeviceSDLAudio::MixBegin( int sampleCount ) +{ + MIX_ClearAllPaintBuffers( sampleCount, false ); +} + + +void CAudioDeviceSDLAudio::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 CAudioDeviceSDLAudio::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 CAudioDeviceSDLAudio::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 CAudioDeviceSDLAudio::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 CAudioDeviceSDLAudio::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 CAudioDeviceSDLAudio::ChannelReset( int entnum, int channelIndex, float distanceMod ) +{ +} + + +void CAudioDeviceSDLAudio::TransferSamples( int end ) +{ + int lpaintedtime = g_paintedtime; + int endtime = end; + + // resumes playback... + + if ( m_pBuffer ) + { + S_TransferStereo16( m_pBuffer, PAINTBUFFER, lpaintedtime, endtime ); + } +} + +void CAudioDeviceSDLAudio::SpatializeChannel( int nSlot, int volume[CCHANVOLUMES/2], int master_vol, const Vector& sourceDir, float gain, float mono ) +{ + VPROF("CAudioDeviceSDLAudio::SpatializeChannel"); + S_SpatializeChannel( /*nSlot,*/ volume, master_vol, &sourceDir, gain, mono ); +} + +void CAudioDeviceSDLAudio::StopAllSounds( void ) +{ +} + + +void CAudioDeviceSDLAudio::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 ); +} + +#endif // !DEDICATED + |