summaryrefslogtreecommitdiff
path: root/engine/audio/private/snd_dev_sdl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engine/audio/private/snd_dev_sdl.cpp')
-rw-r--r--engine/audio/private/snd_dev_sdl.cpp574
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
+