mirror of
https://github.com/dhewm/dhewm3.git
synced 2025-01-18 23:42:26 +00:00
Get rid of all idAudioHardware implementations
This commit is contained in:
parent
18f9793732
commit
b62b033b88
10 changed files with 4 additions and 2202 deletions
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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 ----------------------
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
===================================================================================
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue