Get rid of all idAudioHardware implementations

This commit is contained in:
dhewg 2011-12-14 17:56:04 +01:00
parent 18f9793732
commit b62b033b88
10 changed files with 4 additions and 2202 deletions

View file

@ -21,10 +21,6 @@ cmake_minimum_required(VERSION 2.6)
# ID_NOLANADDRESS -DID_NOLANADDRESS
# ID_MCHECK -DID_MCHECK
# don't add these as options at all
# ALSA remove all sound backends except openal
# OPENAL will always be enabled
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/sys/cmake")
set(CMAKE_SKIP_RPATH ON CACHE BOOL "Skip RPATH" FORCE)
@ -109,8 +105,6 @@ else()
add_definitions(-DID_ENABLE_CURL=0)
endif()
add_definitions(-DNO_ALSA)
# compiler specific flags
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
add_definitions(-pipe)
@ -197,7 +191,7 @@ if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
message(FATAL_ERROR "Unsupported CPU architecture for OSX")
endif()
set(sys_libs ${sys_libs} "-framework Carbon -framework Cocoa -framework OpenGL -framework OpenAL -framework CoreAudio -framework IOKit")
set(sys_libs ${sys_libs} "-framework Carbon -framework Cocoa -framework OpenGL -framework OpenAL -framework IOKit")
else()
if (cpu STREQUAL "x86" AND X86)
add_definitions(-m32)
@ -223,7 +217,6 @@ if (WIN32)
set(sys_libs ${sys_libs}
dinput8
dsound
ksuser
dxguid
dxerr8
@ -655,7 +648,6 @@ if (APPLE)
sys/osx/PickMonitor.cpp
sys/osx/PreferencesDialog.cpp
sys/osx/macosx_guids.cpp
sys/osx/macosx_sound.cpp
sys/osx/DOOMController.mm
sys/osx/macosx_event.mm
sys/osx/macosx_glimp.mm
@ -674,7 +666,6 @@ elseif (WIN32)
sys/win32/win_net.cpp
sys/win32/win_qgl.cpp
sys/win32/win_shared.cpp
sys/win32/win_snd.cpp
sys/win32/win_syscon.cpp
sys/win32/win_taskkeyhook.cpp
sys/win32/win_wndproc.cpp
@ -690,7 +681,6 @@ else()
sys/posix/posix_signal.cpp
sys/posix/posix_threads.cpp
sys/posix/posix_main.cpp
sys/linux/sound.cpp
sys/linux/main.cpp
)

View file

@ -13,7 +13,7 @@ conf_filename='site.conf'
# choose configuration variables which should be saved between runs
# ( we handle all those as strings )
serialized=['CC', 'CXX', 'X86', 'BUILD', 'IDNET_HOST', 'DEDICATED',
'DEBUG_MEMORY', 'LIBC_MALLOC', 'ID_NOLANADDRESS', 'ID_MCHECK', 'OPENAL', 'ALSA',
'DEBUG_MEMORY', 'LIBC_MALLOC', 'ID_NOLANADDRESS', 'ID_MCHECK',
'TARGET_CORE', 'TARGET_GAME', 'TARGET_D3XP', 'TARGET_MONO', 'TARGET_DEMO', 'NOCURL',
'BUILD_ROOT', 'BUILD_GAMEPAK', 'BASEFLAGS' ]
@ -109,12 +109,6 @@ ID_MCHECK (default 2)
note that Doom has it's own block allocator/checking
this should not be considered a replacement, but an additional tool
OPENAL (default 1)
enable OpenAL sound backend support
ALSA (default 1)
enable ALSA sound backend support
SETUP (default 0, not saved)
build a setup. implies release build
@ -179,8 +173,6 @@ LIBC_MALLOC = '1'
ID_NOLANADDRESS = '0'
ID_MCHECK = '2'
BUILD_ROOT = 'build'
OPENAL = '1'
ALSA = '1'
SETUP = '0'
SDK = '0'
NOCONF = '0'
@ -382,7 +374,7 @@ local_idlibpic = 0
# switch between base game build and d3xp game build
local_d3xp = 0
GLOBALS = 'g_env g_env_noopt g_game_env g_os g_cpu g_build ID_MCHECK OPENAL ALSA idlib_objects game_objects local_dedicated local_gamedll local_demo local_idlibpic local_curl local_d3xp OPTCPPFLAGS'
GLOBALS = 'g_env g_env_noopt g_game_env g_os g_cpu g_build ID_MCHECK idlib_objects game_objects local_dedicated local_gamedll local_demo local_idlibpic local_curl local_d3xp OPTCPPFLAGS'
# end general configuration ----------------------

View file

@ -47,9 +47,6 @@ If you have questions concerning this license or the applicable additional terms
#include <mmreg.h>
#define DIRECTINPUT_VERSION 0x0800 // was 0x0700 with the old mssdk
#define DIRECTSOUND_VERSION 0x0800
#include <dsound.h>
#include <dinput.h>
#pragma warning(disable : 4100) // unreferenced formal parameter

View file

@ -68,7 +68,6 @@ const float SND_EPSILON = 1.0f / 32768.0f; // if volume is below this, it wil
const int ROOM_SLICES_IN_BUFFER = 10;
class idAudioHardware;
class idAudioBuffer;
class idWaveFile;
class idSoundCache;
@ -222,38 +221,6 @@ private:
};
/*
===================================================================================
idAudioHardware
===================================================================================
*/
class idAudioHardware {
public:
static idAudioHardware *Alloc();
virtual ~idAudioHardware();
virtual bool Initialize( ) = 0;
virtual bool Lock( void **pDSLockedBuffer, ulong *dwDSLockedBufferSize ) = 0;
virtual bool Unlock( void *pDSLockedBuffer, dword dwDSLockedBufferSize ) = 0;
virtual bool GetCurrentPosition( ulong *pdwCurrentWriteCursor ) = 0;
// try to write as many sound samples to the device as possible without blocking and prepare for a possible new mixing call
// returns wether there is *some* space for writing available
virtual bool Flush( void ) = 0;
virtual void Write( bool flushing ) = 0;
virtual int GetNumberOfSpeakers( void )= 0;
virtual int GetMixBufferSize( void ) = 0;
virtual short* GetMixBuffer( void ) = 0;
};
/*
===================================================================================

View file

@ -1,391 +0,0 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
// OSS sound interface
// http://www.opensound.com/
#include <sys/soundcard.h>
#include "../../idlib/precompiled.h"
#include "../../sound/snd_local.h"
#include "../posix/posix_public.h"
#include "sound.h"
const char *s_driverArgs[] = { "best", "oss", "alsa", NULL };
#ifndef NO_ALSA
static idCVar s_driver( "s_driver", s_driverArgs[0], CVAR_SYSTEM | CVAR_ARCHIVE, "sound driver. 'best' will attempt to use alsa and fallback to OSS if not available", s_driverArgs, idCmdSystem::ArgCompletion_String<s_driverArgs> );
#else
static idCVar s_driver( "s_driver", "oss", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_ROM, "sound driver. only OSS is supported in this build" );
#endif
idAudioHardware *idAudioHardware::Alloc() {
#ifndef NO_ALSA
if ( !strcmp( s_driver.GetString(), "best" ) ) {
idAudioHardwareALSA *test = new idAudioHardwareALSA;
if ( test->DLOpen() ) {
common->Printf( "Alsa is available\n" );
return test;
}
common->Printf( "Alsa is not available\n" );
delete test;
return new idAudioHardwareOSS;
}
if ( !strcmp( s_driver.GetString(), "alsa" ) ) {
return new idAudioHardwareALSA;
}
#endif
return new idAudioHardwareOSS;
}
// OSS sound ----------------------------------------------------
/*
===============
idAudioHardware::~idAudioHardware
===============
*/
idAudioHardware::~idAudioHardware() { }
/*
=================
idAudioHardwareOSS::~idAudioHardwareOSS
=================
*/
idAudioHardwareOSS::~idAudioHardwareOSS() {
Release();
}
/*
=================
idAudioHardwareOSS::Release
=================
*/
void idAudioHardwareOSS::Release( bool bSilent ) {
if (m_audio_fd) {
if (!bSilent) {
common->Printf("------ OSS Sound Shutdown ------\n");
}
if (m_buffer) {
free( m_buffer );
m_buffer = NULL;
m_buffer_size = 0;
}
common->Printf("close sound device\n");
if (close(m_audio_fd) == -1) {
common->Warning( "failed to close sound device: %s", strerror(errno) );
}
m_audio_fd = 0;
if (!bSilent) {
common->Printf("--------------------------------\n");
}
}
}
/*
=================
idAudioHardwareOSS::InitFailed
=================
*/
void idAudioHardwareOSS::InitFailed() {
Release( true );
cvarSystem->SetCVarBool( "s_noSound", true );
common->Warning( "sound subsystem disabled" );
common->Printf( "--------------------------------------\n" );
}
/*
=================
idAudioHardwareOSS::ExtractOSSVersion
=================
*/
void idAudioHardwareOSS::ExtractOSSVersion( int version, idStr &str ) const {
sprintf( str, "%d.%d.%d", ( version & 0xFF0000 ) >> 16, ( version & 0xFF00 ) >> 8, version & 0xFF );
}
/*
=================
idAudioHardwareOSS::Initialize
http://www.4front-tech.com/pguide/index.html
though OSS API docs (1.1) advertise AFMT_S32_LE, AFMT_S16_LE is the only output format I've found in kernel emu10k1 headers
BSD NOTE: With the GNU library, you can use free to free the blocks that memalign, posix_memalign, and valloc return.
That does not work in BSD, however--BSD does not provide any way to free such blocks.
=================
*/
idCVar s_device( "s_dsp", "/dev/dsp", CVAR_SYSTEM | CVAR_ARCHIVE, "" );
bool idAudioHardwareOSS::Initialize( ) {
common->Printf("------ OSS Sound Initialization ------\n");
int requested_sample_format, caps, oss_version;
idStr s_compiled_oss_version, s_oss_version;
struct audio_buf_info info;
memset( &info, 0, sizeof( info ) );
if (m_audio_fd) {
Release();
}
// open device ------------------------------------------------
if ((m_audio_fd = open( s_device.GetString(), O_WRONLY | O_NONBLOCK, 0)) == -1) {
m_audio_fd = 0;
common->Warning( "failed to open sound device '%s': %s", s_device.GetString(), strerror(errno) );
InitFailed();
return false;
}
// make it blocking - so write overruns don't fail with 'Resource temporarily unavailable'
int flags;
if ( ( flags = fcntl( m_audio_fd, F_GETFL ) ) == -1 ) {
common->Warning( "failed fcntl F_GETFL on sound device '%s': %s", s_device.GetString(), strerror( errno ) );
InitFailed();
return false;
}
flags &= ~O_NONBLOCK;
if ( fcntl( m_audio_fd, F_SETFL, flags ) == -1 ) {
common->Warning( "failed to clear O_NONBLOCK on sound device '%s': %s", s_device.GetString(), strerror( errno ) );
InitFailed();
return false;
}
common->Printf("opened sound device '%s'\n", s_device.GetString());
// verify capabilities -----------------------------------------
#if SOUND_VERSION >= 0x040000
// may only be available starting with OSS API v4.0
// http://www.fi.opensound.com/developer/SNDCTL_SYSINFO.html
// NOTE: at OSS API 4.0 headers, replace OSS_SYSINFO with SNDCTL_SYSINFO
oss_sysinfo si;
if ( ioctl( m_audio_fd, OSS_SYSINFO, &si ) == -1 ) {
common->Printf( "ioctl SNDCTL_SYSINFO failed: %s\nthis ioctl is only available in OSS/Linux implementation. If you run OSS/Free, don't bother.", strerror( errno ) );
} else {
common->Printf( "%s: %s %s\n", s_device.GetString(), si.product, si.version );
}
#endif
if ( ioctl( m_audio_fd, SNDCTL_DSP_GETCAPS, &caps ) == -1 ) {
common->Warning( "ioctl SNDCTL_DSP_GETCAPS failed - driver too old?" );
InitFailed();
return false;
}
common->DPrintf("driver rev %d - capabilities %d\n", caps & DSP_CAP_REVISION, caps);
if (ioctl( m_audio_fd, OSS_GETVERSION, &oss_version ) == -1) {
common->Warning( "ioctl OSS_GETVERSION failed" );
InitFailed();
return false;
}
ExtractOSSVersion( oss_version, s_oss_version );
ExtractOSSVersion( SOUND_VERSION, s_compiled_oss_version );
common->DPrintf( "OSS interface version %s - compile time %s\n", s_oss_version.c_str(), s_compiled_oss_version.c_str() );
if (!(caps & DSP_CAP_MMAP)) {
common->Warning( "driver doesn't have DSP_CAP_MMAP capability" );
InitFailed();
return false;
}
if (!(caps & DSP_CAP_TRIGGER)) {
common->Warning( "driver doesn't have DSP_CAP_TRIGGER capability" );
InitFailed();
return false;
}
// sample format -----------------------------------------------
requested_sample_format = AFMT_S16_LE;
m_sample_format = requested_sample_format;
if (ioctl(m_audio_fd, SNDCTL_DSP_SETFMT, &m_sample_format) == -1) {
common->Warning( "ioctl SNDCTL_DSP_SETFMT %d failed: %s", requested_sample_format, strerror(errno) );
InitFailed();
return false;
}
if ( m_sample_format != requested_sample_format ) {
common->Warning( "ioctl SNDCTL_DSP_SETFMT failed to get the requested sample format %d, got %d", requested_sample_format, m_sample_format );
InitFailed();
return false;
}
// channels ----------------------------------------------------
// sanity over number of speakers
if ( idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 6 && idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 2 ) {
common->Warning( "invalid value for s_numberOfSpeakers. Use either 2 or 6" );
idSoundSystemLocal::s_numberOfSpeakers.SetInteger( 2 );
}
m_channels = idSoundSystemLocal::s_numberOfSpeakers.GetInteger();
if ( ioctl( m_audio_fd, SNDCTL_DSP_CHANNELS, &m_channels ) == -1 ) {
common->Warning( "ioctl SNDCTL_DSP_CHANNELS %d failed: %s", idSoundSystemLocal::s_numberOfSpeakers.GetInteger(), strerror(errno) );
InitFailed();
return false;
}
if ( m_channels != (unsigned int)idSoundSystemLocal::s_numberOfSpeakers.GetInteger() ) {
common->Warning( "ioctl SNDCTL_DSP_CHANNELS failed to get the %d requested channels, got %d", idSoundSystemLocal::s_numberOfSpeakers.GetInteger(), m_channels );
if ( m_channels != 2 && idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 2 ) {
// we didn't request 2 channels, some drivers reply 1 channel on error but may still let us still get 2 if properly asked
m_channels = 2;
if ( ioctl( m_audio_fd, SNDCTL_DSP_CHANNELS, &m_channels ) == -1 ) {
common->Warning( "ioctl SNDCTL_DSP_CHANNELS fallback to 2 failed: %s", strerror(errno) );
InitFailed();
return false;
}
}
if ( m_channels == 2 ) {
// tell the system to mix 2 channels
common->Warning( "falling back to stereo" );
idSoundSystemLocal::s_numberOfSpeakers.SetInteger( 2 );
} else {
// disable sound
InitFailed();
return false;
}
}
assert( (int)m_channels == idSoundSystemLocal::s_numberOfSpeakers.GetInteger() );
// sampling rate ------------------------------------------------
m_speed = PRIMARYFREQ;
if ( ioctl( m_audio_fd, SNDCTL_DSP_SPEED, &m_speed ) == -1 ) {
common->Warning( "ioctl SNDCTL_DSP_SPEED %d failed: %s", PRIMARYFREQ, strerror(errno) );
InitFailed();
return false;
}
// instead of an exact match, do a very close to
// there is some horrible Ensonic ES1371 which replies 44101 for a 44100 request
if ( abs( m_speed - PRIMARYFREQ ) > 5 ) {
common->Warning( "ioctl SNDCTL_DSP_SPEED failed to get the requested frequency %d, got %d", PRIMARYFREQ, m_speed );
InitFailed();
return false;
}
common->Printf("%s - bit rate: %d, channels: %d, frequency: %d\n", s_device.GetString(), m_sample_format, m_channels, m_speed);
// output buffer ------------------------------------------------
// allocate a final buffer target, the sound engine locks, writes, and we write back to the device
// we want m_buffer_size ( will have to rename those )
// ROOM_SLICES_IN_BUFFER is fixed ( system default, 10 )
// MIXBUFFER_SAMPLES is the number of samples found in a slice
// each sample is m_channels * sizeof( float ) bytes
// in AsyncUpdate we only write one block at a time, so we'd only need to have a final mix buffer sized of a single block
m_buffer_size = MIXBUFFER_SAMPLES * m_channels * 2;
m_buffer = malloc( m_buffer_size );
common->Printf( "allocated a mix buffer of %d bytes\n", m_buffer_size );
// toggle sound -------------------------------------------------
// toggle off before toggling on. that's what OSS source code samples recommends
int flag = 0;
if (ioctl(m_audio_fd, SNDCTL_DSP_SETTRIGGER, &flag) == -1) {
common->Warning( "ioctl SNDCTL_DSP_SETTRIGGER 0 failed: %s", strerror(errno) );
}
flag = PCM_ENABLE_OUTPUT;
if (ioctl(m_audio_fd, SNDCTL_DSP_SETTRIGGER, &flag) == -1) {
common->Warning( "ioctl SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed: %s", strerror(errno) );
}
common->Printf("--------------------------------------\n");
return true;
}
/*
===============
idAudioHardwareOSS::Flush
===============
*/
bool idAudioHardwareOSS::Flush( void ) {
audio_buf_info ospace;
if ( ioctl( m_audio_fd, SNDCTL_DSP_GETOSPACE, &ospace ) == -1 ) {
Sys_Printf( "ioctl SNDCTL_DSP_GETOSPACE failed: %s\n", strerror( errno ) );
return false;
}
// how many chunks can we write to the audio device right now
m_freeWriteChunks = ( ospace.bytes * MIXBUFFER_CHUNKS ) / ( MIXBUFFER_SAMPLES * m_channels * 2 );
if ( m_writeChunks ) {
// flush out any remaining chunks we could now
Write( true );
}
return ( m_freeWriteChunks > 0 );
}
/*
=================
idAudioHardwareOSS::GetMixBufferSize
=================
*/
int idAudioHardwareOSS::GetMixBufferSize() {
// return MIXBUFFER_SAMPLES * 2 * m_channels;
return m_buffer_size;
}
/*
=================
idAudioHardwareOSS::GetMixBuffer
=================
*/
short* idAudioHardwareOSS::GetMixBuffer() {
return (short *)m_buffer;
}
/*
===============
idAudioHardwareOSS::Write
rely on m_freeWriteChunks which has been set in Flush() before engine did the mixing for this MIXBUFFER_SAMPLE
===============
*/
void idAudioHardwareOSS::Write( bool flushing ) {
assert( m_audio_fd );
int ret;
if ( !flushing && m_writeChunks ) {
// if we write after a new mixing loop, we should have m_writeChunk == 0
// otherwise that last remaining chunk that was never flushed out to the audio device has just been overwritten
Sys_Printf( "idAudioHardwareOSS::Write: %d samples were overflowed and dropped\n", m_writeChunks * MIXBUFFER_SAMPLES / MIXBUFFER_CHUNKS );
}
if ( !flushing ) {
// if running after the mix loop, then we have a full buffer to write out
m_writeChunks = MIXBUFFER_CHUNKS;
}
if ( m_freeWriteChunks == 0 ) {
return;
}
// what to write and how much
uintptr_t pos = (uintptr_t)m_buffer + ( MIXBUFFER_CHUNKS - m_writeChunks ) * m_channels * 2 * MIXBUFFER_SAMPLES / MIXBUFFER_CHUNKS;
int len = Min( m_writeChunks, m_freeWriteChunks ) * m_channels * 2 * MIXBUFFER_SAMPLES / MIXBUFFER_CHUNKS;
assert( len > 0 );
if ( ( ret = write( m_audio_fd, (void*)pos, len ) ) == -1 ) {
Sys_Printf( "write to audio fd failed: %s\n", strerror( errno ) );
return;
}
if ( len != ret ) {
Sys_Printf( "short write to audio fd: wrote %d out of %d\n", ret, m_buffer_size );
return;
}
m_writeChunks -= Min( m_writeChunks, m_freeWriteChunks );
}

View file

@ -1,185 +0,0 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#ifndef ID_SND_BACKENDS
#define ID_SND_BACKENDS
class idAudioHardwareOSS : public idAudioHardware {
// if you can't write MIXBUFFER_SAMPLES all at once to the audio device, split in MIXBUFFER_CHUNKS
static const int MIXBUFFER_CHUNKS = 4;
int m_audio_fd;
int m_sample_format;
unsigned int m_channels;
unsigned int m_speed;
void *m_buffer;
int m_buffer_size;
// counting the loops through the dma buffer
int m_loops;
// how many chunks we have left to write in cases where we need to split
int m_writeChunks;
// how many chunks we can write to the audio device without blocking
int m_freeWriteChunks;
public:
idAudioHardwareOSS() {
m_audio_fd = 0;
m_sample_format = 0;
m_channels = 0;
m_speed = 0;
m_buffer = NULL;
m_buffer_size = 0;
m_loops = 0;
m_writeChunks = 0;
m_freeWriteChunks = 0;
}
virtual ~idAudioHardwareOSS();
bool Initialize( void );
// Linux driver doesn't support memory map API
bool Lock( void **pDSLockedBuffer, ulong *dwDSLockedBufferSize ) { return false; }
bool Unlock( void *pDSLockedBuffer, dword dwDSLockedBufferSize ) { return false; }
bool GetCurrentPosition( ulong *pdwCurrentWriteCursor ) { return false; }
bool Flush();
void Write( bool flushing );
int GetNumberOfSpeakers() { return m_channels; }
int GetMixBufferSize();
short* GetMixBuffer();
private:
void Release( bool bSilent = false );
void InitFailed();
void ExtractOSSVersion( int version, idStr &str ) const;
};
#ifndef NO_ALSA
// libasound2-dev
// the new/old API may be a problem if we are going to dynamically load the asound lib?
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <alsa/asoundlib.h>
typedef const char * ( *pfn_snd_asoundlib_version )( void );
typedef snd_pcm_sframes_t ( *pfn_snd_pcm_avail_update )( snd_pcm_t *pcm );
typedef int ( *pfn_snd_pcm_close )( snd_pcm_t *pcm );
typedef const char * ( *pfn_snd_strerror )( int errnum );
typedef int ( *pfn_snd_pcm_hw_params )( snd_pcm_t *pcm, snd_pcm_hw_params_t *params );
typedef int ( *pfn_snd_pcm_hw_params_any )( snd_pcm_t *pcm, snd_pcm_hw_params_t *params );
typedef int ( *pfn_snd_pcm_hw_params_get_buffer_size )( const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val );
typedef int ( *pfn_snd_pcm_hw_params_set_access )( snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t access );
typedef int ( *pfn_snd_pcm_hw_params_set_buffer_size_min )( snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val );
typedef int ( *pfn_snd_pcm_hw_params_set_channels )( snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val );
typedef int ( *pfn_snd_pcm_hw_params_set_format )( snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t format );
typedef int ( *pfn_snd_pcm_hw_params_set_rate )( snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir );
typedef size_t ( *pfn_snd_pcm_hw_params_sizeof )( void );
typedef int ( *pfn_snd_pcm_open )( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode );
typedef int ( *pfn_snd_pcm_prepare )( snd_pcm_t *pcm );
typedef snd_pcm_state_t ( *pfn_snd_pcm_state )( snd_pcm_t *pcm );
typedef snd_pcm_sframes_t ( *pfn_snd_pcm_writei )( snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size );
#define ALSA_DLSYM(SYM) id_##SYM = ( pfn_##SYM )dlvsym( m_handle, #SYM, "ALSA_0.9" ); if ( !id_##SYM ) { common->Printf( "dlsym "#SYM" failed: %s\n", dlerror() ); Release(); return false; }
class idAudioHardwareALSA : public idAudioHardware {
private:
// if you can't write MIXBUFFER_SAMPLES all at once to the audio device, split in MIXBUFFER_CHUNKS
static const int MIXBUFFER_CHUNKS = 4;
snd_pcm_t *m_pcm_handle;
unsigned int m_channels;
void *m_buffer;
int m_buffer_size;
// how many frames remaining to be written to the device
int m_remainingFrames;
void *m_handle;
public:
idAudioHardwareALSA() {
m_pcm_handle = NULL;
m_channels = 0;
m_buffer = NULL;
m_buffer_size = 0;
m_remainingFrames = 0;
m_handle = NULL;
}
virtual ~idAudioHardwareALSA();
// dlopen the lib ( check minimum version )
bool DLOpen();
bool Initialize( void );
// Linux driver doesn't support memory map API
bool Lock( void **pDSLockedBuffer, ulong *dwDSLockedBufferSize ) { return false; }
bool Unlock( void *pDSLockedBuffer, dword dwDSLockedBufferSize ) { return false; }
bool GetCurrentPosition( ulong *pdwCurrentWriteCursor ) { return false; }
bool Flush();
void Write( bool flushing );
int GetNumberOfSpeakers( void ) { return m_channels; }
int GetMixBufferSize( void );
short* GetMixBuffer( void );
private:
void Release();
void InitFailed();
void PlayTestPattern();
// may be NULL, outdated alsa versions are missing it and we just ignore
pfn_snd_asoundlib_version id_snd_asoundlib_version;
pfn_snd_pcm_avail_update id_snd_pcm_avail_update;
pfn_snd_pcm_close id_snd_pcm_close;
pfn_snd_strerror id_snd_strerror;
pfn_snd_pcm_hw_params id_snd_pcm_hw_params;
pfn_snd_pcm_hw_params_any id_snd_pcm_hw_params_any;
pfn_snd_pcm_hw_params_get_buffer_size id_snd_pcm_hw_params_get_buffer_size;
pfn_snd_pcm_hw_params_set_access id_snd_pcm_hw_params_set_access;
pfn_snd_pcm_hw_params_set_buffer_size_min id_snd_pcm_hw_params_set_buffer_size_min;
pfn_snd_pcm_hw_params_set_channels id_snd_pcm_hw_params_set_channels;
pfn_snd_pcm_hw_params_set_format id_snd_pcm_hw_params_set_format;
pfn_snd_pcm_hw_params_set_rate id_snd_pcm_hw_params_set_rate;
pfn_snd_pcm_hw_params_sizeof id_snd_pcm_hw_params_sizeof;
pfn_snd_pcm_open id_snd_pcm_open;
pfn_snd_pcm_prepare id_snd_pcm_prepare;
pfn_snd_pcm_state id_snd_pcm_state;
pfn_snd_pcm_writei id_snd_pcm_writei;
};
#endif // NO_ALSA
#endif

View file

@ -1,317 +0,0 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "../../idlib/precompiled.h"
#include "../../sound/snd_local.h"
#include "../posix/posix_public.h"
#include "sound.h"
#include <dlfcn.h>
static idCVar s_alsa_pcm( "s_alsa_pcm", "default", CVAR_SYSTEM | CVAR_ARCHIVE, "which alsa pcm device to use. default, hwplug, hw.. see alsa docs" );
static idCVar s_alsa_lib( "s_alsa_lib", "libasound.so.2", CVAR_SYSTEM | CVAR_ARCHIVE, "alsa client sound library" );
/*
===============
idAudioHardwareALSA::DLOpen
===============
*/
bool idAudioHardwareALSA::DLOpen( void ) {
const char *version;
if ( m_handle ) {
return true;
}
common->Printf( "dlopen(%s)\n", s_alsa_lib.GetString() );
if ( !( m_handle = dlopen( s_alsa_lib.GetString(), RTLD_NOW | RTLD_GLOBAL ) ) ) {
common->Printf( "dlopen(%s) failed: %s\n", s_alsa_lib.GetString(), dlerror() );
return false;
}
// print the version if available
id_snd_asoundlib_version = ( pfn_snd_asoundlib_version )dlsym( m_handle, "snd_asoundlib_version" );
if ( !id_snd_asoundlib_version ) {
common->Printf( "dlsym(\"snd_asoundlib_version\") failed: %s\n", dlerror() );
common->Warning( "please consider upgrading alsa to a more recent version." );
} else {
version = id_snd_asoundlib_version();
common->Printf( "asoundlib version: %s\n", version );
}
// dlsym the symbols
ALSA_DLSYM(snd_pcm_avail_update);
ALSA_DLSYM(snd_pcm_close);
ALSA_DLSYM(snd_pcm_hw_params);
ALSA_DLSYM(snd_pcm_hw_params_any);
ALSA_DLSYM(snd_pcm_hw_params_get_buffer_size);
ALSA_DLSYM(snd_pcm_hw_params_set_access);
ALSA_DLSYM(snd_pcm_hw_params_set_buffer_size_min);
ALSA_DLSYM(snd_pcm_hw_params_set_channels);
ALSA_DLSYM(snd_pcm_hw_params_set_format);
ALSA_DLSYM(snd_pcm_hw_params_set_rate);
ALSA_DLSYM(snd_pcm_hw_params_sizeof);
ALSA_DLSYM(snd_pcm_open);
ALSA_DLSYM(snd_pcm_prepare);
ALSA_DLSYM(snd_pcm_state);
ALSA_DLSYM(snd_pcm_writei);
ALSA_DLSYM(snd_strerror);
return true;
}
/*
===============
idAudioHardwareALSA::Release
===============
*/
void idAudioHardwareALSA::Release() {
if ( m_pcm_handle ) {
common->Printf( "close pcm\n" );
id_snd_pcm_close( m_pcm_handle );
m_pcm_handle = NULL;
}
if ( m_buffer ) {
free( m_buffer );
m_buffer = NULL;
}
if ( m_handle ) {
common->Printf( "dlclose\n" );
dlclose( m_handle );
m_handle = NULL;
}
}
/*
=================
idAudioHardwareALSA::InitFailed
=================
*/
void idAudioHardwareALSA::InitFailed() {
Release();
cvarSystem->SetCVarBool( "s_noSound", true );
common->Warning( "sound subsystem disabled\n" );
common->Printf( "--------------------------------------\n" );
}
/*
=====================
idAudioHardwareALSA::Initialize
=====================
*/
bool idAudioHardwareALSA::Initialize( void ) {
int err;
common->Printf( "------ Alsa Sound Initialization -----\n" );
if ( !DLOpen() ) {
InitFailed();
return false;
}
if ( ( err = id_snd_pcm_open( &m_pcm_handle, s_alsa_pcm.GetString(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK ) ) < 0 ) {
common->Printf( "snd_pcm_open SND_PCM_STREAM_PLAYBACK '%s' failed: %s\n", s_alsa_pcm.GetString(), id_snd_strerror( err ) );
InitFailed();
return false;
}
common->Printf( "opened Alsa PCM device %s for playback\n", s_alsa_pcm.GetString() );
// set hardware parameters ----------------------------------------------------------------------
// init hwparams with the full configuration space
snd_pcm_hw_params_t *hwparams = (snd_pcm_hw_params_t *) alloca(id_snd_pcm_hw_params_sizeof());
memset(hwparams, 0, id_snd_pcm_hw_params_sizeof());
if ( ( err = id_snd_pcm_hw_params_any( m_pcm_handle, hwparams ) ) < 0 ) {
common->Printf( "cannot configure the PCM device: %s\n", id_snd_strerror( err ) );
InitFailed();
return false;
}
if ( ( err = id_snd_pcm_hw_params_set_access( m_pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 ) {
common->Printf( "SND_PCM_ACCESS_RW_INTERLEAVED failed: %s\n", id_snd_strerror( err ) );
InitFailed();
return false;
}
if ( ( err = id_snd_pcm_hw_params_set_format( m_pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE ) ) < 0 ) {
common->Printf( "SND_PCM_FORMAT_S16_LE failed: %s\n", id_snd_strerror( err ) );
InitFailed();
return false;
}
// channels
// sanity over number of speakers
if ( idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 6 && idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 2 ) {
common->Warning( "invalid value for s_numberOfSpeakers. Use either 2 or 6" );
idSoundSystemLocal::s_numberOfSpeakers.SetInteger( 2 );
}
m_channels = idSoundSystemLocal::s_numberOfSpeakers.GetInteger();
if ( ( err = id_snd_pcm_hw_params_set_channels( m_pcm_handle, hwparams, m_channels ) ) < 0 ) {
common->Printf( "error setting %d channels: %s\n", m_channels, id_snd_strerror( err ) );
if ( idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 2 ) {
// fallback to stereo if that works
m_channels = 2;
if ( ( err = id_snd_pcm_hw_params_set_channels( m_pcm_handle, hwparams, m_channels ) ) < 0 ) {
common->Printf( "fallback to stereo failed: %s\n", id_snd_strerror( err ) );
InitFailed();
return false;
} else {
common->Printf( "fallback to stereo\n" );
idSoundSystemLocal::s_numberOfSpeakers.SetInteger( 2 );
}
} else {
InitFailed();
return false;
}
}
// set sample rate (frequency)
if ( ( err = id_snd_pcm_hw_params_set_rate( m_pcm_handle, hwparams, PRIMARYFREQ, 0 ) ) < 0 ) {
common->Printf( "failed to set 44.1KHz rate: %s - try ( +set s_alsa_pcm plughw:0 ? )\n", id_snd_strerror( err ) );
InitFailed();
return false;
}
// have enough space in the input buffer for our MIXBUFFER_SAMPLE feedings and async ticks
snd_pcm_uframes_t frames;
frames = MIXBUFFER_SAMPLES + MIXBUFFER_SAMPLES / 3;
if ( ( err = id_snd_pcm_hw_params_set_buffer_size_min( m_pcm_handle, hwparams, &frames ) ) < 0 ) {
common->Printf( "buffer size select failed: %s\n", id_snd_strerror( err ) );
InitFailed();
return false;
}
// apply parameters
if ( ( err = id_snd_pcm_hw_params( m_pcm_handle, hwparams ) ) < 0 ) {
common->Printf( "snd_pcm_hw_params failed: %s\n", id_snd_strerror( err ) );
InitFailed();
return false;
}
// check the buffer size
if ( ( err = id_snd_pcm_hw_params_get_buffer_size( hwparams, &frames ) ) < 0 ) {
common->Printf( "snd_pcm_hw_params_get_buffer_size failed: %s\n", id_snd_strerror( err ) );
} else {
common->Printf( "device buffer size: %lu frames ( %lu bytes )\n", ( long unsigned int )frames, frames * m_channels * 2 );
}
// TODO: can use swparams to setup the device so it doesn't underrun but rather loops over
// snd_pcm_sw_params_set_stop_threshold
// To get alsa to just loop on underruns. set the swparam stop_threshold to equal buffer size. The sound buffer will just loop and never throw an xrun.
// allocate the final mix buffer
m_buffer_size = MIXBUFFER_SAMPLES * m_channels * 2;
m_buffer = malloc( m_buffer_size );
common->Printf( "allocated a mix buffer of %d bytes\n", m_buffer_size );
#ifdef _DEBUG
// verbose the state
snd_pcm_state_t curstate = id_snd_pcm_state( m_pcm_handle );
assert( curstate == SND_PCM_STATE_PREPARED );
#endif
common->Printf( "--------------------------------------\n" );
return true;
}
/*
===============
idAudioHardwareALSA::~idAudioHardwareALSA
===============
*/
idAudioHardwareALSA::~idAudioHardwareALSA() {
common->Printf( "----------- Alsa Shutdown ------------\n" );
Release();
common->Printf( "--------------------------------------\n" );
}
/*
=================
idAudioHardwareALSA::GetMixBufferSize
=================
*/
int idAudioHardwareALSA::GetMixBufferSize() {
return m_buffer_size;
}
/*
=================
idAudioHardwareALSA::GetMixBuffer
=================
*/
short* idAudioHardwareALSA::GetMixBuffer() {
return (short *)m_buffer;
}
/*
===============
idAudioHardwareALSA::Flush
===============
*/
bool idAudioHardwareALSA::Flush( void ) {
int ret;
snd_pcm_state_t state;
state = id_snd_pcm_state( m_pcm_handle );
if ( state != SND_PCM_STATE_RUNNING && state != SND_PCM_STATE_PREPARED ) {
if ( ( ret = id_snd_pcm_prepare( m_pcm_handle ) ) < 0 ) {
Sys_Printf( "failed to recover from SND_PCM_STATE_XRUN: %s\n", id_snd_strerror( ret ) );
cvarSystem->SetCVarBool( "s_noSound", true );
return false;
}
Sys_Printf( "preparing audio device for output\n" );
}
Write( true );
return true;
}
/*
===============
idAudioHardwareALSA::Write
rely on m_freeWriteChunks which has been set in Flush() before engine did the mixing for this MIXBUFFER_SAMPLE
===============
*/
void idAudioHardwareALSA::Write( bool flushing ) {
if ( !flushing && m_remainingFrames ) {
// if we write after a new mixing loop, we should have m_writeChunk == 0
// otherwise that last remaining chunk that was never flushed out to the audio device has just been overwritten
Sys_Printf( "idAudioHardwareALSA::Write: %d frames overflowed and dropped\n", m_remainingFrames );
}
if ( !flushing ) {
// if running after the mix loop, then we have a full buffer to write out
m_remainingFrames = MIXBUFFER_SAMPLES;
}
if ( m_remainingFrames == 0 ) {
return;
}
// write the max frames you can in one shot - we need to write it all out in Flush() calls before the next Write() happens
uintptr_t pos = (uintptr_t)m_buffer + ( MIXBUFFER_SAMPLES - m_remainingFrames ) * m_channels * 2;
snd_pcm_sframes_t frames = id_snd_pcm_writei( m_pcm_handle, (void*)pos, m_remainingFrames );
if ( frames < 0 ) {
if ( frames != -EAGAIN ) {
Sys_Printf( "snd_pcm_writei %d frames failed: %s\n", m_remainingFrames, id_snd_strerror( frames ) );
}
return;
}
m_remainingFrames -= frames;
}

View file

@ -1,433 +0,0 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "../../idlib/precompiled.h"
#include "../../sound/snd_local.h"
#include <Carbon/Carbon.h>
#include <CoreAudio/CoreAudio.h>
idCVar s_device( "s_device", "-1", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "Sound device to use. -1 for default device" );
class idAudioHardwareOSX : public idAudioHardware {
public:
idAudioHardwareOSX();
~idAudioHardwareOSX();
bool Initialize( );
// OSX driver doesn't support memory map API
bool Lock( void **pDSLockedBuffer, ulong *dwDSLockedBufferSize ) { return false; }
bool Unlock( void *pDSLockedBuffer, dword dwDSLockedBufferSize ) { return false; }
bool GetCurrentPosition( ulong *pdwCurrentWriteCursor ) { return false; }
int GetMixBufferSize( void ) { return 0; }
int GetNumberOfSpeakers( void );
// OSX driver doesn't support write API
bool Flush( void ) { return false; }
void Write( bool ) { }
short* GetMixBuffer( void ) { return NULL; }
private:
AudioDeviceID selectedDevice;
bool activeIOProc;
void Reset( void );
void InitFailed( void );
const char* ExtractStatus( OSStatus status );
void GetAvailableNominalSampleRates( void );
// AudioDevicePropertyListenerProc
static OSStatus DeviceListener( AudioDeviceID inDevice,
UInt32 inChannel,
Boolean isInput,
AudioDevicePropertyID inPropertyID,
void* inClientData );
// AudioDeviceIOProc
static OSStatus DeviceIOProc( AudioDeviceID inDevice,
const AudioTimeStamp* inNow,
const AudioBufferList* inInputData,
const AudioTimeStamp* inInputTime,
AudioBufferList* outOutputData,
const AudioTimeStamp* inOutputTime,
void* inClientData );
};
/*
==========
iAudioHardware::Alloc
==========
*/
idAudioHardware *idAudioHardware::Alloc() { return new idAudioHardwareOSX; }
/*
==========
idAudioHardware::~idAudioHardware
==========
*/
idAudioHardware::~idAudioHardware() { }
/*
==========
idAudioHardwareOSX::idAudioHardwareOSX
==========
*/
idAudioHardwareOSX::idAudioHardwareOSX() {
selectedDevice = kAudioDeviceUnknown;
activeIOProc = false;
}
/*
==========
idAudioHardwareOSX::~idAudioHardwareOSX
==========
*/
idAudioHardwareOSX::~idAudioHardwareOSX() {
Reset();
}
/*
==========
idAudioHardwareOSX::Reset
==========
*/
void idAudioHardwareOSX::Reset() {
OSStatus status;
if ( activeIOProc ) {
status = AudioDeviceStop( selectedDevice, DeviceIOProc );
if ( status != kAudioHardwareNoError ) {
common->Warning( "idAudioHardwareOSX::Reset: AudioDeviceStop failed. status: %s", ExtractStatus( status ) );
}
status = AudioDeviceRemoveIOProc( selectedDevice, DeviceIOProc );
if ( status != kAudioHardwareNoError ) {
common->Warning( "idAudioHardwareOSX::Reset: AudioDeviceRemoveIOProc failed. status %s\n", ExtractStatus( status ) );
}
activeIOProc = false;
}
selectedDevice = kAudioDeviceUnknown;
AudioHardwareUnload();
}
/*
=================
idAudioHardwareOSX::InitFailed
=================
*/
void idAudioHardwareOSX::InitFailed() {
Reset();
cvarSystem->SetCVarBool( "s_noSound", true );
common->Warning( "sound subsystem disabled" );
common->Printf( "------------------------------------------------\n" );
}
/*
==========
idAudioHardwareOSX::DeviceListener
==========
*/
OSStatus idAudioHardwareOSX::DeviceListener( AudioDeviceID inDevice,
UInt32 inChannel,
Boolean isInput,
AudioDevicePropertyID inPropertyID,
void* inClientData) {
common->Printf( "DeviceListener\n" );
return kAudioHardwareNoError;
}
/*
==========
idAudioHardwareOSX::DeviceIOProc
==========
*/
OSStatus idAudioHardwareOSX::DeviceIOProc( AudioDeviceID inDevice,
const AudioTimeStamp* inNow,
const AudioBufferList* inInputData,
const AudioTimeStamp* inInputTime,
AudioBufferList* outOutputData,
const AudioTimeStamp* inOutputTime,
void* inClientData ) {
// setup similar to async thread
Sys_EnterCriticalSection();
soundSystem->AsyncMix( (int)inOutputTime->mSampleTime, (float*)outOutputData->mBuffers[ 0 ].mData );
Sys_LeaveCriticalSection();
// doom mixes sound to -32768.0f 32768.0f range, scale down to -1.0f 1.0f
SIMDProcessor->Mul( (Float32*)outOutputData->mBuffers[ 0 ].mData, 1.0f / 32768.0f, (Float32*)outOutputData->mBuffers[ 0 ].mData, MIXBUFFER_SAMPLES * 2 );
return kAudioHardwareNoError;
}
/*
==========
idAudioHardwareOSX::ExtractStatus
==========
*/
const char* idAudioHardwareOSX::ExtractStatus( OSStatus status ) {
static char buf[ sizeof( OSStatus ) + 1 ];
strncpy( buf, (const char *)&status, sizeof( OSStatus ) );
buf[ sizeof( OSStatus ) ] = '\0';
return buf;
}
/*
==========
idAudioHardwareOSX::Initialize
==========
*/
bool idAudioHardwareOSX::Initialize( ) {
UInt32 size;
OSStatus status;
int i, deviceCount;
AudioDeviceID *deviceList;
char buf[ 1024 ];
status = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, &size, NULL );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioHardwareGetPropertyInfo kAudioHardwarePropertyDevices failed. status: %s", ExtractStatus( status ) );
InitFailed();
return false;
}
deviceCount = size / sizeof( AudioDeviceID );
if ( !deviceCount ) {
common->Printf( "No sound device found\n" );
InitFailed();
return false;
}
deviceList = (AudioDeviceID*)malloc( size );
status = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &size, deviceList );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioHardwareGetProperty kAudioHardwarePropertyDevices failed. status: %s", ExtractStatus( status ) );
free( deviceList );
InitFailed();
return false;
}
common->Printf( "%d sound device(s)\n", deviceCount );
for( i = 0; i < deviceCount; i++ ) {
size = 1024;
status = AudioDeviceGetProperty( deviceList[ i ], 0, false, kAudioDevicePropertyDeviceName, &size, buf );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioDeviceGetProperty kAudioDevicePropertyDeviceName %d failed. status: %s", i, ExtractStatus( status ) );
free( deviceList );
InitFailed();
return false;
}
common->Printf( " %d: ID %d, %s - ", i, deviceList[ i ], buf );
size = 1024;
status = AudioDeviceGetProperty( deviceList[ i ], 0, false, kAudioDevicePropertyDeviceManufacturer, &size, buf );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioDeviceGetProperty kAudioDevicePropertyDeviceManufacturer %d failed. status: %s", i, ExtractStatus( status ) );
free( deviceList );
InitFailed();
return false;
}
common->Printf( "%s\n", buf );
}
if ( s_device.GetInteger() != -1 && s_device.GetInteger() < deviceCount ) {
selectedDevice = deviceList[ s_device.GetInteger() ];
common->Printf( "s_device: device ID %d\n", selectedDevice );
} else {
size = sizeof( selectedDevice );
status = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &size, &selectedDevice );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioHardwareGetProperty kAudioHardwarePropertyDefaultOutputDevice failed. status: %s", ExtractStatus( status ) );
free( deviceList );
InitFailed();
return false;
}
common->Printf( "select default device, ID %d\n", selectedDevice );
}
free( deviceList );
deviceList = NULL;
/*
// setup a listener to watch for changes to properties
status = AudioDeviceAddPropertyListener( selectedDevice, 0, false, kAudioDeviceProcessorOverload, DeviceListener, this );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioDeviceAddPropertyListener kAudioDeviceProcessorOverload failed. status: %s", ExtractStatus( status ) );
InitFailed();
return;
}
*/
Float64 sampleRate;
size = sizeof( sampleRate );
status = AudioDeviceGetProperty( selectedDevice, 0, false, kAudioDevicePropertyNominalSampleRate, &size, &sampleRate );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyNominalSampleRate failed. status: %s", selectedDevice, ExtractStatus( status ) );
InitFailed();
return false;
}
common->Printf( "current nominal rate: %g\n", sampleRate );
if ( sampleRate != PRIMARYFREQ ) {
GetAvailableNominalSampleRates();
sampleRate = PRIMARYFREQ;
common->Printf( "setting rate to: %g\n", sampleRate );
status = AudioDeviceSetProperty( selectedDevice, NULL, 0, false, kAudioDevicePropertyNominalSampleRate, size, &sampleRate );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioDeviceSetProperty %d kAudioDevicePropertyNominalSampleRate %g failed. status: %s", selectedDevice, sampleRate, ExtractStatus( status ) );
InitFailed();
return false;
}
}
UInt32 frameSize;
size = sizeof( UInt32 );
status = AudioDeviceGetProperty( selectedDevice, 0, false, kAudioDevicePropertyBufferFrameSize, &size, &frameSize );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyBufferFrameSize failed.status: %s", selectedDevice, ExtractStatus( status ) );
InitFailed();
return false;
}
common->Printf( "current frame size: %d\n", frameSize );
// get the allowed frame size range
AudioValueRange frameSizeRange;
size = sizeof( AudioValueRange );
status = AudioDeviceGetProperty( selectedDevice, 0, false, kAudioDevicePropertyBufferFrameSizeRange, &size, &frameSizeRange );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyBufferFrameSizeRange failed. status: %s", selectedDevice, ExtractStatus( status ) );
InitFailed();
return false;
}
common->Printf( "frame size allowed range: %g %g\n", frameSizeRange.mMinimum, frameSizeRange.mMaximum );
if ( frameSizeRange.mMaximum < MIXBUFFER_SAMPLES ) {
common->Warning( "can't obtain the required frame size of %d bits", MIXBUFFER_SAMPLES );
InitFailed();
return false;
}
if ( frameSize != (unsigned int)MIXBUFFER_SAMPLES ) {
frameSize = MIXBUFFER_SAMPLES;
common->Printf( "setting frame size to: %d\n", frameSize );
size = sizeof( frameSize );
status = AudioDeviceSetProperty( selectedDevice, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, size, &frameSize );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioDeviceSetProperty %d kAudioDevicePropertyBufferFrameSize failed. status: %s", selectedDevice, ExtractStatus( status ) );
InitFailed();
return false;
}
}
if ( idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 2 ) {
common->Warning( "only stereo sound currently supported" );
idSoundSystemLocal::s_numberOfSpeakers.SetInteger( 2 );
}
UInt32 channels[ 2 ];
size = 2 * sizeof( UInt32 );
status = AudioDeviceGetProperty( selectedDevice, 0, false, kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyPreferredChannelsForStereo failed. status: %s", selectedDevice, ExtractStatus( status ) );
InitFailed();
return false;
}
common->Printf( "using stereo channel IDs %d %d\n", channels[ 0 ], channels[ 1 ] );
status = AudioDeviceAddIOProc( selectedDevice, DeviceIOProc, NULL );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioDeviceAddIOProc failed. status: %s", ExtractStatus( status ) );
InitFailed();
return false;
}
activeIOProc = true;
status = AudioDeviceStart( selectedDevice, DeviceIOProc );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioDeviceStart failed. status: %s", ExtractStatus( status ) );
InitFailed();
return false;
}
/*
// allocate the mix buffer
// it has the space for ROOM_SLICES_IN_BUFFER DeviceIOProc loops
mixBufferSize = dwSpeakers * dwSampleSize * dwPrimaryBitRate * ROOM_SLICES_IN_BUFFER / 8;
mixBuffer = malloc( mixBufferSize );
memset( mixBuffer, 0, mixBufferSize );
*/
return true;
}
/*
==========
idAudioHardwareOSX::GetAvailableNominalSampleRates
==========
*/
void idAudioHardwareOSX::GetAvailableNominalSampleRates( void ) {
UInt32 size;
OSStatus status;
int i, rangeCount;
AudioValueRange *rangeArray;
status = AudioDeviceGetPropertyInfo( selectedDevice, 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, NULL );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioDeviceGetPropertyInfo %d kAudioDevicePropertyAvailableNominalSampleRates failed. status: %s", selectedDevice, ExtractStatus( status ) );
return;
}
rangeCount = size / sizeof( AudioValueRange );
rangeArray = (AudioValueRange *)malloc( size );
common->Printf( "%d possible rate(s)\n", rangeCount );
status = AudioDeviceGetProperty( selectedDevice, 0, false, kAudioDevicePropertyAvailableNominalSampleRates, &size, rangeArray );
if ( status != kAudioHardwareNoError ) {
common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyAvailableNominalSampleRates failed. status: %s", selectedDevice, ExtractStatus( status ) );
free( rangeArray );
return;
}
for( i = 0; i < rangeCount; i++ ) {
common->Printf( " %d: min %g max %g\n", i, rangeArray[ i ].mMinimum, rangeArray[ i ].mMaximum );
}
free( rangeArray );
}
/*
==========
idAudioHardwareOSX::GetNumberOfSpeakers
==========
*/
int idAudioHardwareOSX::GetNumberOfSpeakers() {
return idSoundSystemLocal::s_numberOfSpeakers.GetInteger();
}

View file

@ -211,6 +211,7 @@ if ( local_dedicated == 0 ):
else:
sys_string += ' \
stub/stub_gl.cpp \
stub/openal_stub.cpp \
linux/dedicated.cpp'
sys_list = scons_utils.BuildList( 'sys', sys_string )
@ -231,9 +232,6 @@ local_env = g_env.Clone()
if ( local_dedicated == 1 ):
local_env.Append( CPPDEFINES = [ 'ID_DEDICATED' ] )
# don't enable openal or alsa for a dedicated server binary
OPENAL = '0'
ALSA = '0'
if ( local_gamedll == 1 ):
local_env.Append( CPPDEFINES = [ '__DOOM_DLL__' ] )
@ -244,18 +242,6 @@ if ( local_demo == 1 ):
if ( local_curl == 0 ):
local_env.Append( CPPDEFINES = [ 'ID_ENABLE_CURL=0' ] )
sound_env = local_env.Clone()
sound_list = [ '../linux/sound.cpp' ]
if ( OPENAL != '1' ):
sound_env.Append( CPPDEFINES = 'ID_OPENAL=0' )
sound_list.append( '../../sys/stub/openal_stub.cpp' )
if ( g_os == "Linux" and ALSA != '0' ):
sound_list.append( '../../sys/linux/sound_alsa.cpp' )
else:
sound_env.Append( CPPDEFINES = 'NO_ALSA' )
sound_lib = sound_env.StaticLibrary( 'sound', sound_list )
local_env.Append( LIBS = [ 'pthread', 'jpeg', 'vorbisfile' ] )
if ( local_dedicated == 0 ):
@ -269,13 +255,10 @@ if ( local_curl == 1 ):
if ( local_dedicated == 0 ):
local_env.Append( LIBS = [ 'X11', 'Xext', 'Xxf86vm' ] ) # 'Xxf86dga',
local_env.Append( LIBPATH = [ '/usr/X11R6/lib' ] )
if ( OPENAL == '1' ):
local_env.Append( LIBS = [ 'openal' ] )
source_list = core_list
source_list += idlib_objects
source_list += sound_lib
if ( local_gamedll == 0 ):
source_list += game_objects

View file

@ -1,801 +0,0 @@
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "../../idlib/precompiled.h"
#pragma hdrstop
// DirectX SDK
#include <dxerr8.h>
#include <ks.h>
#include <ksmedia.h>
#include "../../sound/snd_local.h"
#include "win_local.h"
#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
#define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } }
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
class idAudioBufferWIN32 : public idAudioBuffer {
public:
idAudioBufferWIN32( LPDIRECTSOUNDBUFFER apDSBuffer, dword dwDSBufferSize, idWaveFile* pWaveFile=NULL );
~idAudioBufferWIN32();
int FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB, bool bRepeatWavIfBufferLarger );
bool Lock( void **pDSLockedBuffer, ulong *dwDSLockedBufferSize );
bool Unlock(void *pDSLockedBuffer, dword dwDSLockedBufferSize );
bool GetCurrentPosition( ulong *pdwCurrentWriteCursor );
int Play( dword dwPriority=0, dword dwFlags=0 );
int Stop( void );
int Reset( void );
bool IsSoundPlaying( void );
void SetVolume( float x);
idWaveFile* m_pWaveFile;
private:
LPDIRECTSOUNDBUFFER m_apDSBuffer;
dword m_dwDSBufferSize;
int RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, bool* pbWasRestored );
};
class idAudioHardwareWIN32 : public idAudioHardware {
public:
idAudioHardwareWIN32();
~idAudioHardwareWIN32();
bool Initialize( );
bool InitializeSpeakers( byte *buffer, int bufferSize, dword dwPrimaryFreq, dword dwPrimaryBitRate, dword dwSpeakers );
void SetPrimaryBufferFormat( dword dwPrimaryFreq, dword dwPrimaryBitRate, dword dwSpeakers );
int Create( idWaveFile* pWaveFile, idAudioBuffer** ppiab );
int Create( idAudioBuffer** ppSound, const char* strWaveFileName, dword dwCreationFlags = 0 );
int CreateFromMemory( idAudioBufferWIN32** ppSound, byte* pbData, ulong ulDataSize, waveformatextensible_t *pwfx, dword dwCreationFlags = 0 );
bool Lock( void **pDSLockedBuffer, ulong *dwDSLockedBufferSize );
bool Unlock( void *pDSLockedBuffer, dword dwDSLockedBufferSize );
bool GetCurrentPosition( ulong *pdwCurrentWriteCursor );
int GetNumberOfSpeakers() { return numSpeakers; }
int GetMixBufferSize() { return MIXBUFFER_SAMPLES * blockAlign; }
// WIN32 driver doesn't support write API
bool Flush( void ) { return true; }
void Write( bool ) { }
short* GetMixBuffer( void ) { return NULL; }
private:
LPDIRECTSOUND m_pDS;
LPDIRECTSOUNDBUFFER pDSBPrimary;
idAudioBufferWIN32 *speakers;
int numSpeakers;
int bitsPerSample;
int bufferSize; // allocate buffer handed over to DirectSound
int blockAlign; // channels * bits per sample / 8: sound frame size
};
idAudioHardware *idAudioHardware::Alloc() { return new idAudioHardwareWIN32; }
idAudioHardware::~idAudioHardware() {}
/*
================
idAudioHardwareWIN32::idAudioHardware
================
*/
idAudioHardwareWIN32::idAudioHardwareWIN32() {
m_pDS = NULL;
pDSBPrimary = NULL;
speakers = NULL;
}
/*
================
idAudioHardwareWIN32::~idAudioHardware
================
*/
idAudioHardwareWIN32::~idAudioHardwareWIN32() {
SAFE_DELETE( speakers );
SAFE_RELEASE( pDSBPrimary );
SAFE_RELEASE( m_pDS );
}
/*
===============
idAudioHardwareWIN32::Initialize
===============
*/
bool idAudioHardwareWIN32::Initialize( void ) {
int hr;
bufferSize = 0;
numSpeakers = 0;
blockAlign = 0;
SAFE_RELEASE( m_pDS );
// Create IDirectSound using the primary sound device
if( FAILED( hr = DirectSoundCreate( NULL, &m_pDS, NULL ) )) {
return false;
}
// Set primary buffer format
SetPrimaryBufferFormat( PRIMARYFREQ, 16, idSoundSystemLocal::s_numberOfSpeakers.GetInteger() );
return true;
}
/*
===============
idAudioHardwareWIN32::InitializeSpeakers
===============
*/
bool idAudioHardwareWIN32::InitializeSpeakers( byte *speakerData, int bufferSize, dword dwPrimaryFreq, dword dwPrimaryBitRate, dword dwSpeakers ) {
if ( dwSpeakers == 2 ) {
WAVEFORMATEXTENSIBLE wfx;
ZeroMemory( &wfx, sizeof(WAVEFORMATEXTENSIBLE) );
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
wfx.Format.nChannels = 2;
wfx.Format.nSamplesPerSec = dwPrimaryFreq;
wfx.Format.wBitsPerSample = dwPrimaryBitRate;
wfx.Format.nBlockAlign = wfx.Format.wBitsPerSample / 8 * wfx.Format.nChannels;
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
wfx.Format.cbSize = sizeof(WAVEFORMATEX);
CreateFromMemory( &speakers, speakerData, bufferSize, (waveformatextensible_t *)&wfx );
common->Printf("sound: STEREO\n");
} else {
WAVEFORMATEXTENSIBLE waveFormatPCMEx;
ZeroMemory( &waveFormatPCMEx, sizeof(WAVEFORMATEXTENSIBLE) );
waveFormatPCMEx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
waveFormatPCMEx.Format.nChannels = 6;
waveFormatPCMEx.Format.nSamplesPerSec = dwPrimaryFreq;
waveFormatPCMEx.Format.wBitsPerSample = dwPrimaryBitRate;
waveFormatPCMEx.Format.nBlockAlign = waveFormatPCMEx.Format.wBitsPerSample / 8 * waveFormatPCMEx.Format.nChannels;
waveFormatPCMEx.Format.nAvgBytesPerSec = waveFormatPCMEx.Format.nSamplesPerSec * waveFormatPCMEx.Format.nBlockAlign;
waveFormatPCMEx.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
// SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
// SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
// SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
waveFormatPCMEx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; // Specify PCM
waveFormatPCMEx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE);
waveFormatPCMEx.Samples.wValidBitsPerSample = 16;
CreateFromMemory( &speakers, speakerData, bufferSize, (waveformatextensible_t *)&waveFormatPCMEx );
common->Printf("sound: MULTICHANNEL\n");
}
if (!speakers) {
return false;
}
speakers->Play(0,DSBPLAY_LOOPING);
return true;
}
/*
===============
idAudioHardwareWIN32::SetPrimaryBufferFormat
Set primary buffer to a specified format
For example, to set the primary buffer format to 22kHz stereo, 16-bit
then: dwPrimaryChannels = 2
dwPrimaryFreq = 22050,
dwPrimaryBitRate = 16
===============
*/
void idAudioHardwareWIN32::SetPrimaryBufferFormat( dword dwPrimaryFreq, dword dwPrimaryBitRate, dword dwSpeakers ) {
HRESULT hr;
if( m_pDS == NULL ) {
return;
}
ulong cfgSpeakers;
m_pDS->GetSpeakerConfig( &cfgSpeakers );
DSCAPS dscaps;
dscaps.dwSize = sizeof(DSCAPS);
m_pDS->GetCaps(&dscaps);
if (dscaps.dwFlags & DSCAPS_EMULDRIVER) {
return;
}
// Get the primary buffer
DSBUFFERDESC dsbd;
ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
dsbd.dwBufferBytes = 0;
dsbd.lpwfxFormat = NULL;
// Obtain write-primary cooperative level.
if( FAILED( hr = m_pDS->SetCooperativeLevel(win32.hWnd, DSSCL_PRIORITY ) ) ) {
DXTRACE_ERR( TEXT("SetPrimaryBufferFormat"), hr );
return;
}
if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &pDSBPrimary, NULL ) ) ) {
return;
}
if ( dwSpeakers == 6 && (cfgSpeakers == DSSPEAKER_5POINT1 || cfgSpeakers == DSSPEAKER_SURROUND) ) {
WAVEFORMATEXTENSIBLE waveFormatPCMEx;
ZeroMemory( &waveFormatPCMEx, sizeof(WAVEFORMATEXTENSIBLE) );
waveFormatPCMEx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
waveFormatPCMEx.Format.nChannels = 6;
waveFormatPCMEx.Format.nSamplesPerSec = dwPrimaryFreq;
waveFormatPCMEx.Format.wBitsPerSample = (WORD) dwPrimaryBitRate;
waveFormatPCMEx.Format.nBlockAlign = waveFormatPCMEx.Format.wBitsPerSample / 8 * waveFormatPCMEx.Format.nChannels;
waveFormatPCMEx.Format.nAvgBytesPerSec = waveFormatPCMEx.Format.nSamplesPerSec * waveFormatPCMEx.Format.nBlockAlign;
waveFormatPCMEx.dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
// SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT |
// SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY |
// SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT
waveFormatPCMEx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; // Specify PCM
waveFormatPCMEx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE);
waveFormatPCMEx.Samples.wValidBitsPerSample = 16;
if( FAILED( hr = pDSBPrimary->SetFormat((WAVEFORMATEX*)&waveFormatPCMEx) ) ) {
DXTRACE_ERR( TEXT("SetPrimaryBufferFormat"), hr );
return;
}
numSpeakers = 6; // force it to think 5.1
blockAlign = waveFormatPCMEx.Format.nBlockAlign;
} else {
if (dwSpeakers == 6) {
common->Printf("sound: hardware reported unable to use multisound, defaulted to stereo\n");
}
WAVEFORMATEX wfx;
ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = dwPrimaryFreq;
wfx.wBitsPerSample = (WORD) dwPrimaryBitRate;
wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.cbSize = sizeof(WAVEFORMATEX);
if( FAILED( hr = pDSBPrimary->SetFormat(&wfx) ) ) {
return;
}
numSpeakers = 2; // force it to think stereo
blockAlign = wfx.nBlockAlign;
}
byte *speakerData;
bufferSize = MIXBUFFER_SAMPLES * sizeof(word) * numSpeakers * ROOM_SLICES_IN_BUFFER;
speakerData = (byte *)Mem_Alloc( bufferSize );
memset( speakerData, 0, bufferSize );
InitializeSpeakers( speakerData, bufferSize, dwPrimaryFreq, dwPrimaryBitRate, numSpeakers );
}
/*
===============
idAudioHardwareWIN32::Create
===============
*/
int idAudioHardwareWIN32::Create( idAudioBuffer** ppSound,
const char* strWaveFileName,
dword dwCreationFlags ) {
int hr;
LPDIRECTSOUNDBUFFER apDSBuffer = NULL;
dword dwDSBufferSize = NULL;
idWaveFile* pWaveFile = NULL;
if( m_pDS == NULL )
return -1;
if( strWaveFileName == NULL || ppSound == NULL )
return -1;
pWaveFile = new idWaveFile();
pWaveFile->Open( strWaveFileName, NULL );
if( pWaveFile->GetOutputSize() == 0 ) {
// Wave is blank, so don't create it.
hr = E_FAIL;
goto LFail;
}
// Make the DirectSound buffer the same size as the wav file
dwDSBufferSize = pWaveFile->GetOutputSize();
// Create the direct sound buffer, and only request the flags needed
// since each requires some overhead and limits if the buffer can
// be hardware accelerated
DSBUFFERDESC dsbd;
memset( &dsbd, 0, sizeof(DSBUFFERDESC) );
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = dwCreationFlags;
dsbd.dwBufferBytes = dwDSBufferSize;
dsbd.guid3DAlgorithm = GUID_NULL;
dsbd.lpwfxFormat = (WAVEFORMATEX*)&pWaveFile->mpwfx;
// DirectSound is only guarenteed to play PCM data. Other
// formats may or may not work depending the sound card driver.
if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer, NULL ) ) )
return -1;
// Create the sound
*ppSound = new idAudioBufferWIN32( apDSBuffer, dwDSBufferSize, pWaveFile );
pWaveFile->Close();
return 0;
LFail:
// Cleanup
SAFE_DELETE( pWaveFile );
return -1;
}
/*
===============
idAudioHardwareWIN32::Create
===============
*/
int idAudioHardwareWIN32::Create( idWaveFile* pWaveFile, idAudioBuffer** ppiab ) {
int hr;
LPDIRECTSOUNDBUFFER apDSBuffer = NULL;
dword dwDSBufferSize = NULL;
if( m_pDS == NULL )
return -1;
if( pWaveFile == NULL )
return -1;
*ppiab = NULL;
if( pWaveFile->GetOutputSize() == 0 ) {
// Wave is blank, so don't create it.
hr = E_FAIL;
goto LFail;
}
// Make the DirectSound buffer the same size as the wav file
dwDSBufferSize = pWaveFile->GetOutputSize();
// Create the direct sound buffer, and only request the flags needed
// since each requires some overhead and limits if the buffer can
// be hardware accelerated
DSBUFFERDESC dsbd;
memset( &dsbd, 0, sizeof(DSBUFFERDESC) );
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = 0;
dsbd.dwBufferBytes = dwDSBufferSize;
dsbd.guid3DAlgorithm = GUID_NULL;
dsbd.lpwfxFormat = (WAVEFORMATEX*)&pWaveFile->mpwfx;
// DirectSound is only guarenteed to play PCM data. Other
// formats may or may not work depending the sound card driver.
if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer, NULL ) ) )
return -1;
// Create the sound
*ppiab = new idAudioBufferWIN32( apDSBuffer, dwDSBufferSize, pWaveFile );
return 0;
LFail:
// Cleanup
SAFE_DELETE( pWaveFile );
return -1;
}
//-----------------------------------------------------------------------------
// Name: idAudioHardwareWIN32::CreateFromMemory()
// Desc:
//-----------------------------------------------------------------------------
int idAudioHardwareWIN32::CreateFromMemory( idAudioBufferWIN32** ppSound,
byte* pbData,
ulong ulDataSize,
waveformatextensible_t* pwfx,
dword dwCreationFlags ) {
int hr;
LPDIRECTSOUNDBUFFER apDSBuffer = NULL;
dword dwDSBufferSize = NULL;
idWaveFile* pWaveFile = NULL;
if( m_pDS == NULL )
return -1;
if( pbData == NULL || ppSound == NULL )
return -1;
pWaveFile = new idWaveFile();
pWaveFile->OpenFromMemory( (short *)pbData, ulDataSize, (waveformatextensible_t *)pwfx);
// Make the DirectSound buffer the same size as the wav file
dwDSBufferSize = ulDataSize;
// Create the direct sound buffer, and only request the flags needed
// since each requires some overhead and limits if the buffer can
// be hardware accelerated
DSBUFFERDESC dsbd;
memset( &dsbd, 0, sizeof(DSBUFFERDESC) );
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = dwCreationFlags | DSBCAPS_GETCURRENTPOSITION2;
dsbd.dwBufferBytes = dwDSBufferSize;
dsbd.guid3DAlgorithm = GUID_NULL;
dsbd.lpwfxFormat = (WAVEFORMATEX *)pwfx;
if( FAILED( hr = m_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer, NULL ) ) )
return -1;
// Create the sound
*ppSound = new idAudioBufferWIN32( apDSBuffer, dwDSBufferSize, pWaveFile );
return S_OK;
}
/*
===============
idAudioHardwareWIN32::Lock
===============
*/
bool idAudioHardwareWIN32::Lock( void **pDSLockedBuffer, ulong *dwDSLockedBufferSize ) {
if (speakers) {
return speakers->Lock( pDSLockedBuffer, dwDSLockedBufferSize );
}
return false;
}
/*
===============
idAudioHardwareWIN32::Unlock
===============
*/
bool idAudioHardwareWIN32::Unlock(void *pDSLockedBuffer, dword dwDSLockedBufferSize ) {
if (speakers) {
return speakers->Unlock( pDSLockedBuffer, dwDSLockedBufferSize );
}
return false;
}
/*
===============
idAudioHardwareWIN32::GetCurrentPosition
===============
*/
bool idAudioHardwareWIN32::GetCurrentPosition( ulong *pdwCurrentWriteCursor ) {
if (speakers) {
return speakers->GetCurrentPosition( pdwCurrentWriteCursor );
}
return false;
}
/*
===============
idAudioBufferWIN32::idAudioBuffer
===============
*/
idAudioBufferWIN32::idAudioBufferWIN32( LPDIRECTSOUNDBUFFER apDSBuffer, dword dwDSBufferSize, idWaveFile* pWaveFile ) {
m_apDSBuffer = apDSBuffer;
m_dwDSBufferSize = dwDSBufferSize;
m_pWaveFile = pWaveFile;
if (pWaveFile) {
FillBufferWithSound( m_apDSBuffer, false );
m_apDSBuffer->SetCurrentPosition(0);
}
}
/*
===============
idAudioBufferWIN32::~idAudioBuffer
===============
*/
idAudioBufferWIN32::~idAudioBufferWIN32() {
SAFE_DELETE(m_pWaveFile);
SAFE_RELEASE( m_apDSBuffer );
m_pWaveFile = NULL;
m_apDSBuffer = NULL;
}
/*
===============
idAudioBufferWIN32::FillBufferWithSound
===============
*/
int idAudioBufferWIN32::FillBufferWithSound( LPDIRECTSOUNDBUFFER pDSB, bool bRepeatWavIfBufferLarger ) {
int hr;
void* pDSLockedBuffer = NULL; // Pointer to locked buffer memory
ulong dwDSLockedBufferSize = 0; // Size of the locked DirectSound buffer
int dwWavDataRead = 0; // Amount of data read from the wav file
if( pDSB == NULL )
return -1;
// we may not even have a wavefile
if (m_pWaveFile==NULL) {
return -1;
}
// Make sure we have focus, and we didn't just switch in from
// an app which had a DirectSound device
if( FAILED( hr = RestoreBuffer( pDSB, NULL ) ) ) {
DXTRACE_ERR( TEXT("RestoreBuffer"), hr );
return -1;
}
// Lock the buffer down
if( FAILED( hr = pDSB->Lock( 0, m_dwDSBufferSize, &pDSLockedBuffer, &dwDSLockedBufferSize, NULL, NULL, 0L ) ) ) {
DXTRACE_ERR( TEXT("Lock"), hr );
return -1;
}
// Reset the wave file to the beginning
m_pWaveFile->ResetFile();
if( FAILED( hr = m_pWaveFile->Read( (byte*) pDSLockedBuffer, dwDSLockedBufferSize, &dwWavDataRead ) ) ) {
return DXTRACE_ERR( TEXT("Read"), hr );
}
if( dwWavDataRead == 0 ) {
// Wav is blank, so just fill with silence
memset( pDSLockedBuffer, (byte)(m_pWaveFile->mpwfx.Format.wBitsPerSample == 8 ? 128 : 0 ), dwDSLockedBufferSize );
} else if( dwWavDataRead < (int)dwDSLockedBufferSize ) {
// If the wav file was smaller than the DirectSound buffer,
// we need to fill the remainder of the buffer with data
if( bRepeatWavIfBufferLarger ) {
// Reset the file and fill the buffer with wav data
int dwReadSoFar = dwWavDataRead; // From previous call above.
while( dwReadSoFar < (int)dwDSLockedBufferSize ) {
// This will keep reading in until the buffer is full
// for very short files
if( FAILED( hr = m_pWaveFile->ResetFile() ) ) {
return DXTRACE_ERR( TEXT("ResetFile"), hr );
}
hr = m_pWaveFile->Read( (byte*)pDSLockedBuffer + dwReadSoFar, dwDSLockedBufferSize - dwReadSoFar, &dwWavDataRead );
if( FAILED(hr) ) {
return DXTRACE_ERR( TEXT("Read"), hr );
}
dwReadSoFar += dwWavDataRead;
}
} else {
// Don't repeat the wav file, just fill in silence
memset( (byte*) pDSLockedBuffer + dwWavDataRead, (byte)(m_pWaveFile->mpwfx.Format.wBitsPerSample == 8 ? 128 : 0 ), dwDSLockedBufferSize - dwWavDataRead);
}
}
// Unlock the buffer, we don't need it anymore.
pDSB->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
return S_OK;
}
/*
===============
idAudioBufferWIN32::RestoreBuffer
Desc: Restores the lost buffer. *pbWasRestored returns true if the buffer was
restored. It can also NULL if the information is not needed.
===============
*/
int idAudioBufferWIN32::RestoreBuffer( LPDIRECTSOUNDBUFFER pDSB, bool* pbWasRestored ) {
int hr;
if( pDSB == NULL ) {
return -1;
}
if( pbWasRestored ) {
*pbWasRestored = false;
}
ulong dwStatus;
if( FAILED( hr = pDSB->GetStatus( &dwStatus ) ) ) {
return DXTRACE_ERR( TEXT("GetStatus"), hr );
}
if( dwStatus & DSBSTATUS_BUFFERLOST ) {
// Since the app could have just been activated, then
// DirectSound may not be giving us control yet, so
// the restoring the buffer may fail.
// If it does, sleep until DirectSound gives us control.
do {
hr = pDSB->Restore();
if( hr == DSERR_BUFFERLOST ) {
Sleep( 10 );
}
hr = pDSB->Restore();
} while( hr );
if( pbWasRestored != NULL ) {
*pbWasRestored = true;
}
return S_OK;
} else {
return S_FALSE;
}
}
/*
===============
idAudioBufferWIN32::Play
Desc: Plays the sound using voice management flags. Pass in DSBPLAY_LOOPING
in the dwFlags to loop the sound
===============
*/
int idAudioBufferWIN32::Play( dword dwPriority, dword dwFlags ) {
int hr;
bool bRestored;
if( m_apDSBuffer == NULL ) {
return -1;
}
// Restore the buffer if it was lost
if( FAILED( hr = RestoreBuffer( m_apDSBuffer, &bRestored ) ) ) {
common->Error( TEXT("RestoreBuffer"), hr );
}
if( bRestored ) {
// The buffer was restored, so we need to fill it with new data
if( FAILED( hr = FillBufferWithSound( m_apDSBuffer, false ) ) ) {
common->Error( TEXT("FillBufferWithSound"), hr );
}
// Make DirectSound do pre-processing on sound effects
Reset();
}
m_apDSBuffer->Play( 0, dwPriority, dwFlags );
return 0;
}
/*
===============
idAudioBufferWIN32::Stop
Desc: Stops the sound from playing
===============
*/
int idAudioBufferWIN32::Stop() {
if( this == NULL || m_apDSBuffer == NULL ) {
return -1;
}
m_apDSBuffer->Stop();
return 0;
}
/*
===============
idAudioBufferWIN32::Reset
Desc: Reset all of the sound buffers
===============
*/
int idAudioBufferWIN32::Reset() {
if( m_apDSBuffer == NULL ) {
return -1;
}
m_apDSBuffer->SetCurrentPosition( 0 );
return 0;
}
/*
===============
idAudioBufferWIN32::IsSoundPlaying
Desc: Checks to see if a buffer is playing and returns true if it
===============
*/
bool idAudioBufferWIN32::IsSoundPlaying( ) {
if( m_apDSBuffer == NULL ) {
return false;
}
if( m_apDSBuffer ) {
ulong dwStatus = 0;
m_apDSBuffer->GetStatus( &dwStatus );
if ( dwStatus & DSBSTATUS_PLAYING ) {
return true;
}
}
return false;
}
/*
===============
idAudioBufferWIN32::Lock
===============
*/
bool idAudioBufferWIN32::Lock( void **pDSLockedBuffer, ulong *dwDSLockedBufferSize ) {
int hr;
// Restore the buffer if it was lost
bool bRestored;
if( FAILED( hr = RestoreBuffer( m_apDSBuffer, &bRestored ) ) ) {
return false;
}
// Lock the DirectSound buffer
if( FAILED( hr = m_apDSBuffer->Lock( 0, m_dwDSBufferSize, pDSLockedBuffer, dwDSLockedBufferSize, NULL, NULL, 0 ) ) ) {
return false;
}
return true;
}
/*
===============
idAudioBufferWIN32::Unlock
===============
*/
bool idAudioBufferWIN32::Unlock(void *pDSLockedBuffer, dword dwDSLockedBufferSize ) {
// Unlock the DirectSound buffer
m_apDSBuffer->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );
return true;
}
/*
===============
idAudioBufferWIN32::GetCurrentPosition
===============
*/
bool idAudioBufferWIN32::GetCurrentPosition( ulong *pdwCurrentWriteCursor ) {
int hr;
// Make sure we have focus, and we didn't just switch in from
// an app which had a DirectSound device
if( FAILED( hr = RestoreBuffer( m_apDSBuffer, NULL ) ) ) {
DXTRACE_ERR( TEXT("RestoreBuffer"), hr );
return false;
}
if( FAILED( hr = m_apDSBuffer->GetCurrentPosition( NULL, pdwCurrentWriteCursor ) ) ) {
return false;
}
return true;
}
/*
===============
idAudioBufferWIN32::SetVolume
===============
*/
void idAudioBufferWIN32::SetVolume( float x) {
if (m_apDSBuffer) {
m_apDSBuffer->SetVolume(x);
}
}