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_NOLANADDRESS -DID_NOLANADDRESS
# ID_MCHECK -DID_MCHECK # 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_MODULE_PATH "${CMAKE_SOURCE_DIR}/sys/cmake")
set(CMAKE_SKIP_RPATH ON CACHE BOOL "Skip RPATH" FORCE) set(CMAKE_SKIP_RPATH ON CACHE BOOL "Skip RPATH" FORCE)
@ -109,8 +105,6 @@ else()
add_definitions(-DID_ENABLE_CURL=0) add_definitions(-DID_ENABLE_CURL=0)
endif() endif()
add_definitions(-DNO_ALSA)
# compiler specific flags # compiler specific flags
if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang") if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
add_definitions(-pipe) 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") message(FATAL_ERROR "Unsupported CPU architecture for OSX")
endif() 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() else()
if (cpu STREQUAL "x86" AND X86) if (cpu STREQUAL "x86" AND X86)
add_definitions(-m32) add_definitions(-m32)
@ -223,7 +217,6 @@ if (WIN32)
set(sys_libs ${sys_libs} set(sys_libs ${sys_libs}
dinput8 dinput8
dsound
ksuser ksuser
dxguid dxguid
dxerr8 dxerr8
@ -655,7 +648,6 @@ if (APPLE)
sys/osx/PickMonitor.cpp sys/osx/PickMonitor.cpp
sys/osx/PreferencesDialog.cpp sys/osx/PreferencesDialog.cpp
sys/osx/macosx_guids.cpp sys/osx/macosx_guids.cpp
sys/osx/macosx_sound.cpp
sys/osx/DOOMController.mm sys/osx/DOOMController.mm
sys/osx/macosx_event.mm sys/osx/macosx_event.mm
sys/osx/macosx_glimp.mm sys/osx/macosx_glimp.mm
@ -674,7 +666,6 @@ elseif (WIN32)
sys/win32/win_net.cpp sys/win32/win_net.cpp
sys/win32/win_qgl.cpp sys/win32/win_qgl.cpp
sys/win32/win_shared.cpp sys/win32/win_shared.cpp
sys/win32/win_snd.cpp
sys/win32/win_syscon.cpp sys/win32/win_syscon.cpp
sys/win32/win_taskkeyhook.cpp sys/win32/win_taskkeyhook.cpp
sys/win32/win_wndproc.cpp sys/win32/win_wndproc.cpp
@ -690,7 +681,6 @@ else()
sys/posix/posix_signal.cpp sys/posix/posix_signal.cpp
sys/posix/posix_threads.cpp sys/posix/posix_threads.cpp
sys/posix/posix_main.cpp sys/posix/posix_main.cpp
sys/linux/sound.cpp
sys/linux/main.cpp sys/linux/main.cpp
) )

View file

@ -13,7 +13,7 @@ conf_filename='site.conf'
# choose configuration variables which should be saved between runs # choose configuration variables which should be saved between runs
# ( we handle all those as strings ) # ( we handle all those as strings )
serialized=['CC', 'CXX', 'X86', 'BUILD', 'IDNET_HOST', 'DEDICATED', 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', 'TARGET_CORE', 'TARGET_GAME', 'TARGET_D3XP', 'TARGET_MONO', 'TARGET_DEMO', 'NOCURL',
'BUILD_ROOT', 'BUILD_GAMEPAK', 'BASEFLAGS' ] 'BUILD_ROOT', 'BUILD_GAMEPAK', 'BASEFLAGS' ]
@ -109,12 +109,6 @@ ID_MCHECK (default 2)
note that Doom has it's own block allocator/checking note that Doom has it's own block allocator/checking
this should not be considered a replacement, but an additional tool 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) SETUP (default 0, not saved)
build a setup. implies release build build a setup. implies release build
@ -179,8 +173,6 @@ LIBC_MALLOC = '1'
ID_NOLANADDRESS = '0' ID_NOLANADDRESS = '0'
ID_MCHECK = '2' ID_MCHECK = '2'
BUILD_ROOT = 'build' BUILD_ROOT = 'build'
OPENAL = '1'
ALSA = '1'
SETUP = '0' SETUP = '0'
SDK = '0' SDK = '0'
NOCONF = '0' NOCONF = '0'
@ -382,7 +374,7 @@ local_idlibpic = 0
# switch between base game build and d3xp game build # switch between base game build and d3xp game build
local_d3xp = 0 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 ---------------------- # end general configuration ----------------------

View file

@ -47,9 +47,6 @@ If you have questions concerning this license or the applicable additional terms
#include <mmreg.h> #include <mmreg.h>
#define DIRECTINPUT_VERSION 0x0800 // was 0x0700 with the old mssdk #define DIRECTINPUT_VERSION 0x0800 // was 0x0700 with the old mssdk
#define DIRECTSOUND_VERSION 0x0800
#include <dsound.h>
#include <dinput.h> #include <dinput.h>
#pragma warning(disable : 4100) // unreferenced formal parameter #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; const int ROOM_SLICES_IN_BUFFER = 10;
class idAudioHardware;
class idAudioBuffer; class idAudioBuffer;
class idWaveFile; class idWaveFile;
class idSoundCache; 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: else:
sys_string += ' \ sys_string += ' \
stub/stub_gl.cpp \ stub/stub_gl.cpp \
stub/openal_stub.cpp \
linux/dedicated.cpp' linux/dedicated.cpp'
sys_list = scons_utils.BuildList( 'sys', sys_string ) sys_list = scons_utils.BuildList( 'sys', sys_string )
@ -231,9 +232,6 @@ local_env = g_env.Clone()
if ( local_dedicated == 1 ): if ( local_dedicated == 1 ):
local_env.Append( CPPDEFINES = [ 'ID_DEDICATED' ] ) 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 ): if ( local_gamedll == 1 ):
local_env.Append( CPPDEFINES = [ '__DOOM_DLL__' ] ) local_env.Append( CPPDEFINES = [ '__DOOM_DLL__' ] )
@ -244,18 +242,6 @@ if ( local_demo == 1 ):
if ( local_curl == 0 ): if ( local_curl == 0 ):
local_env.Append( CPPDEFINES = [ 'ID_ENABLE_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' ] ) local_env.Append( LIBS = [ 'pthread', 'jpeg', 'vorbisfile' ] )
if ( local_dedicated == 0 ): if ( local_dedicated == 0 ):
@ -269,13 +255,10 @@ if ( local_curl == 1 ):
if ( local_dedicated == 0 ): if ( local_dedicated == 0 ):
local_env.Append( LIBS = [ 'X11', 'Xext', 'Xxf86vm' ] ) # 'Xxf86dga', local_env.Append( LIBS = [ 'X11', 'Xext', 'Xxf86vm' ] ) # 'Xxf86dga',
local_env.Append( LIBPATH = [ '/usr/X11R6/lib' ] ) local_env.Append( LIBPATH = [ '/usr/X11R6/lib' ] )
if ( OPENAL == '1' ):
local_env.Append( LIBS = [ 'openal' ] ) local_env.Append( LIBS = [ 'openal' ] )
source_list = core_list source_list = core_list
source_list += idlib_objects source_list += idlib_objects
source_list += sound_lib
if ( local_gamedll == 0 ): if ( local_gamedll == 0 ):
source_list += game_objects 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);
}
}