Merge OpenAL branch

This commit is contained in:
Daniel Gibson 2012-04-28 15:58:50 +00:00
commit 53d54b1f23
13 changed files with 1566 additions and 134 deletions

View file

@ -30,6 +30,10 @@ WITH_CDA=yes
# dependencies to libogg, libvorbis and libvorbisfile.
WITH_OGG=yes
# Enables the optional OpenAL sound systems. Adds
# a dependencx to libopenal
WITH_OPENAL=yes
# Enables retexturing support. Adds a dependency to
# libjpeg
WITH_RETEXTURING=yes
@ -182,6 +186,11 @@ release/quake2 : CFLAGS += -DOGG
release/quake2 : LDFLAGS += -lvorbis -lvorbisfile -logg
endif
ifeq ($(WITH_OPENAL),yes)
release/quake2 : CFLAGS += -DUSE_OPENAL -DDEFAULT_OPENAL_DRIVER='"libopenal.so.1"'
release/quake2 : LDFLAGS += -lopenal
endif
ifeq ($(WITH_ZIP),yes)
release/quake2 : CFLAGS += -DZIP
release/quake2 : LDFLAGS += -lz
@ -325,6 +334,7 @@ CLIENT_OBJS_ := \
src/client/menu/menu.o \
src/client/menu/qmenu.o \
src/client/menu/videomenu.o \
src/client/sound/snd_al.o \
src/client/sound/snd_dma.o \
src/client/sound/snd_mem.o \
src/client/sound/snd_mix.o \
@ -371,6 +381,7 @@ CLIENT_OBJS_ := \
src/unix/hunk.o \
src/unix/main.o \
src/unix/network.o \
src/unix/qal.o \
src/unix/signalhandler.o \
src/unix/system.o \
src/unix/vid.o

56
README
View file

@ -38,10 +38,11 @@ Content of this file:
2.4 Console variables
3. Configuration
3.1 Widescreen setup
3.2 Video
3.3 Input
3.4 Sound
3.1 Video
3.2 Input
3.3 Sound
3.3.1 The classic sound system
3.3.2 The OpenAL sound system
4. Bugreports
@ -338,14 +339,23 @@ There are some cvar to adjust:
3.3 Sound
---------
Quake II featured one of the best sound systems of it's time but sadly it was
totaly broken. Therefore id Software rewrote it once, later it was rewritten
again for the linux port. That fixed the most visible problems, but the code was
just crap and broke again as time passed and sound on PCs evolved.
For Yamagi Quake II 3.0 the sound system was overhauled, featuring a complete
code audit of the upper layers with many bugfixes and memory leak plugs.
The backend was rewritten from scratch. This should solve most if not all
problems.
Quake II featured one of the best sound systems of it's time (for example it had
support for realtime calculated stereo effects) but sadly it was totaly broken.
Therefore id Software rewrote it once, later it was rewritten again for the
linux port. That fixed the most visible problems, but the code was just crap and
broke again as time passed and sound on PCs evolved. For Yamagi Quake II 3.0 the
sound system was overhauled, featuring a complete code audit of the upper layers
with many bugfixes and memory leak plugs. The backend was rewritten from
scratch. This should solve most if not all problems. Yamagi Quake II 4.20
featured an optional OpenAL sound system, enabling better stereo calculations
and even surround support.
3.3.1 The classic sound system
------------------------------
This is the original sound implementation, as used in the first release of Quake
II in 1997. It featured stereo calculations for most samples. It's disabled by
default and can be reenables by setting "s_openal" to "0", followed by
"snd_restart. Common problems are:
- The earthquake sound sample is distorted
This is not a fault of the sound code but of the sound sample itself.
@ -370,6 +380,28 @@ problems.
card 0
}
3.3.2 The OpenAL sound system
-----------------------------
This is a sound system based upon the popular OpenAL audio library. It features
surround playback which gives a huge improvement in sound quality and gameplay
experience. It's enables by default, but can be disabled by setting "s_openal"
to "0", followed by a "snd_restart". To work correctly it's in the need of a
correctly configured OpenAL! OpenAL is configured in the file ~/.alsoftrc (for
the openal-soft implementation, other implementations may vary). The most
important options (tested with OpenAL Soft 1.14) are:
- channels = surround51 -> Enable 5.1 surround support. Other values are "mono",
"stereo", "quad", "surround61" and "surround71".
- stereodup = true -> If set to "true" all raw stereo samples (in Quake II the
background music and video sounds) are duplicated behind the listener.
Otherwise they're played only through the front speakers.
- resampler = cubic -> Use cubic resampling. While this requires more cpu power
than the default linear resamling it's highly recommended since Quake II has
several hard to resamples sound effects. Especially the earthquake sound can
distort if a low quality resampler is employed!
===============================================================================
4. Bugreports

View file

@ -37,6 +37,10 @@ typedef struct {
int loopstart;
int speed; /* not needed, because converted on load? */
int width;
#if USE_OPENAL
int size;
int bufnum;
#endif
int stereo;
byte data[1]; /* variable sized */
} sfxcache_t;
@ -87,6 +91,10 @@ typedef struct {
int master_vol; /* 0-255 master volume */
qboolean fixed_origin; /* use origin instead of fetching entnum's origin */
qboolean autosound; /* from an entity->sound, cleared each frame */
#if USE_OPENAL
int autoframe;
int srcnum;
#endif
} channel_t;
typedef struct {
@ -98,6 +106,13 @@ typedef struct {
int dataofs; /* chunk starts this many bytes from file start */
} wavinfo_t;
typedef enum {
SS_NOT = 0, // soundsystem not started
SS_DMA, // soundsystem started, using DMA/SDL
SS_OAL // soundsystem started, using OpenAL
} sndstarted_t;
extern sndstarted_t sound_started;
/* initializes cycling through a DMA
buffer and returns information on it */
@ -113,6 +128,7 @@ void SNDDMA_Submit(void);
#define MAX_CHANNELS 32
extern channel_t channels[MAX_CHANNELS];
extern int s_numchannels;
extern int paintedtime;
extern int s_rawend;
@ -133,6 +149,7 @@ extern cvar_t *s_khz;
extern cvar_t *s_show;
extern cvar_t *s_mixahead;
extern cvar_t *s_testsound;
extern cvar_t *s_ambient;
wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength);
void S_InitScaletable (void);
@ -146,5 +163,29 @@ channel_t *S_PickChannel(int entnum, int entchannel);
/* spatializes a channel */
void S_Spatialize(channel_t *ch);
void S_BuildSoundList( int *sounds );
#if USE_OPENAL
// this stuff was taken from Q2Pro
// only begin attenuating sound volumes when outside the FULLVOLUME range
#define SOUND_FULLVOLUME 80
#define SOUND_LOOPATTENUATE 0.003
// number of buffers in flight (needed for ogg)
extern int active_buffers;
// for snd_al.c - copied from Q2Pro and adapted
void AL_SoundInfo( void );
qboolean AL_Init( void );
void AL_Shutdown( void );
sfxcache_t *AL_UploadSfx( sfx_t *s, wavinfo_t *s_info, byte *data );
void AL_DeleteSfx( sfx_t *s );
void AL_StopChannel( channel_t *ch );
void AL_PlayChannel( channel_t *ch );
void AL_StopAllChannels( void );
void AL_Update( void );
void AL_RawSamples( int samples, int rate, int width, int channels, byte *data, float volume );
#endif
#endif

534
src/client/sound/snd_al.c Normal file
View file

@ -0,0 +1,534 @@
/*
* Copyright (C) 1997-2005 Id Software, Inc.
* (C) 2010 skuller.net
* (C) 2005 Stuart Dalton (badcdev@gmail.com)
*
* This program 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 2 of the License, or (at
* your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Most of these functions are from the Q2Pro project, and some are from
* zeq2. We adapted them to work with Yamagi Quake II
*
* This is an OpenAL backend for the Quake II Soundsystem.
*
* =======================================================================
*/
#ifdef USE_OPENAL
#include "../header/client.h"
#include "../../unix/header/qal.h"
#include "header/local.h"
#include "header/vorbis.h"
// translates from AL coordinate system to quake
#define AL_UnpackVector(v) -v[1],v[2],-v[0]
#define AL_CopyVector(a,b) ((b)[0]=-(a)[1],(b)[1]=(a)[2],(b)[2]=-(a)[0])
// OpenAL implementation should support at least this number of sources
#define MIN_CHANNELS 16
qboolean streamPlaying;
int active_buffers;
static ALuint streamSource;
static ALuint underwaterFilter;
static ALuint s_srcnums[MAX_CHANNELS-1];
static int s_framecount;
// Forward Declarations
static void S_AL_StreamUpdate( void );
static void S_AL_StreamDie( void );
// /Forward Declarations
static void AL_InitStreamSource() {
qalSourcei (streamSource, AL_BUFFER, 0 );
qalSourcei (streamSource, AL_LOOPING, AL_FALSE );
qalSource3f(streamSource, AL_POSITION, 0.0, 0.0, 0.0);
qalSource3f(streamSource, AL_VELOCITY, 0.0, 0.0, 0.0);
qalSource3f(streamSource, AL_DIRECTION, 0.0, 0.0, 0.0);
qalSourcef (streamSource, AL_ROLLOFF_FACTOR, 0.0 );
qalSourcei (streamSource, AL_SOURCE_RELATIVE, AL_TRUE );
}
static void AL_InitUnderwaterFilter()
{
// Generate a filter
qalGenFilters(1, &underwaterFilter);
if (qalGetError() != AL_NO_ERROR)
{
Com_Printf("Couldn't generate an OpenAL filter!\n");
return;
}
// Low pass filter for underwater effect
qalFilteri(underwaterFilter, AL_FILTER_TYPE, AL_FILTER_LOWPASS);
if (qalGetError() != AL_NO_ERROR)
{
Com_Printf("Low pass filter is not supported!\n");
return;
}
// The effect
qalFilterf(underwaterFilter, AL_LOWPASS_GAIN, 1.5);
qalFilterf(underwaterFilter, AL_LOWPASS_GAINHF, 0.25);
}
qboolean AL_Init( void ) {
int i;
if( !QAL_Init() ) {
Com_Printf( "ERROR: OpenAL failed to initialize.\n" );
return false;
}
// check for linear distance extension
if( !qalIsExtensionPresent( "AL_EXT_LINEAR_DISTANCE" ) ) {
Com_Printf( "ERROR: Required AL_EXT_LINEAR_DISTANCE extension is missing.\n" );
goto fail;
}
// generate source names
qalGetError();
qalGenSources( 1, &streamSource );
if( qalGetError() != AL_NO_ERROR )
{
Com_Printf( "ERROR: Couldn't get a single Source.\n" );
goto fail;
} else {
// -1 because we already got one channel for streaming
for( i = 0; i < MAX_CHANNELS - 1; i++ ) {
qalGenSources( 1, &s_srcnums[i] );
if( qalGetError() != AL_NO_ERROR ) {
break;
}
}
if( i < MIN_CHANNELS - 1 ) {
Com_Printf( "ERROR: Required at least %d sources, but got %d.\n", MIN_CHANNELS, i+1 );
goto fail;
}
}
s_numchannels = i;
AL_InitStreamSource();
AL_InitUnderwaterFilter();
Com_Printf("Number of OpenAL sources: %d\n\n", s_numchannels);
return true;
fail:
QAL_Shutdown();
return false;
}
void AL_Shutdown( void ) {
Com_Printf( "Shutting down OpenAL.\n" );
qalDeleteSources(1, &streamSource);
qalDeleteFilters(1, &underwaterFilter);
if( s_numchannels ) {
// delete source names
qalDeleteSources( s_numchannels, s_srcnums );
memset( s_srcnums, 0, sizeof( s_srcnums ) );
s_numchannels = 0;
}
S_AL_StreamDie();
QAL_Shutdown();
}
sfxcache_t *AL_UploadSfx( sfx_t *s, wavinfo_t *s_info, byte *data ) {
sfxcache_t *sc;
ALsizei size = s_info->samples * s_info->width;
ALenum format = s_info->width == 2 ? AL_FORMAT_MONO16 : AL_FORMAT_MONO8;
ALuint name;
if( !size ) {
return NULL;
}
qalGetError();
qalGenBuffers( 1, &name );
qalBufferData( name, format, data, size, s_info->rate );
active_buffers++;
if( qalGetError() != AL_NO_ERROR ) {
return NULL;
}
// allocate placeholder sfxcache
sc = s->cache = Z_TagMalloc(sizeof(*sc), 0);
sc->length = s_info->samples * 1000 / s_info->rate; // in msec
sc->loopstart = s_info->loopstart;
sc->width = s_info->width;
sc->size = size;
sc->bufnum = name;
return sc;
}
void AL_DeleteSfx( sfx_t *s ) {
sfxcache_t *sc;
ALuint name;
sc = s->cache;
if( !sc ) {
return;
}
name = sc->bufnum;
qalDeleteBuffers( 1, &name );
active_buffers--;
}
void AL_StopChannel( channel_t *ch ) {
if (s_show->value > 1)
Com_Printf("%s: %s\n", __func__, ch->sfx->name );
// stop it
qalSourceStop( ch->srcnum );
qalSourcei( ch->srcnum, AL_BUFFER, AL_NONE );
memset (ch, 0, sizeof(*ch));
}
static void AL_Spatialize( channel_t *ch ) {
vec3_t origin;
// anything coming from the view entity will always be full volume
// no attenuation = no spatialization
if( ch->entnum == -1 || ch->entnum == cl.playernum + 1 || !ch->dist_mult ) {
VectorCopy( listener_origin, origin );
} else if( ch->fixed_origin ) {
VectorCopy( ch->origin, origin );
} else {
CL_GetEntitySoundOrigin( ch->entnum, origin );
}
qalSource3f( ch->srcnum, AL_POSITION, AL_UnpackVector( origin ) );
}
void AL_PlayChannel( channel_t *ch ) {
sfxcache_t *sc = ch->sfx->cache;
if (s_show->value > 1)
Com_Printf("%s: %s\n", __func__, ch->sfx->name );
ch->srcnum = s_srcnums[ch - channels];
qalGetError();
qalSourcei( ch->srcnum, AL_BUFFER, sc->bufnum );
qalSourcei( ch->srcnum, AL_LOOPING, ch->autosound ? AL_TRUE : AL_FALSE );
qalSourcef( ch->srcnum, AL_GAIN, ch->master_vol );
qalSourcef( ch->srcnum, AL_REFERENCE_DISTANCE, SOUND_FULLVOLUME );
qalSourcef( ch->srcnum, AL_MAX_DISTANCE, 8192 );
qalSourcef( ch->srcnum, AL_ROLLOFF_FACTOR, ch->dist_mult * ( 8192 - SOUND_FULLVOLUME ) );
AL_Spatialize( ch );
// play it
qalSourcePlay( ch->srcnum );
if( qalGetError() != AL_NO_ERROR ) {
AL_StopChannel( ch );
}
}
void AL_StopAllChannels( void ) {
int i;
channel_t *ch;
ch = channels;
for( i = 0; i < s_numchannels; i++, ch++ ) {
if (!ch->sfx)
continue;
AL_StopChannel( ch );
}
s_rawend = 0;
S_AL_StreamDie();
}
static channel_t *AL_FindLoopingSound( int entnum, sfx_t *sfx ) {
int i;
channel_t *ch;
ch = channels;
for( i = 0; i < s_numchannels; i++, ch++ ) {
if( !ch->sfx )
continue;
if( !ch->autosound )
continue;
if( ch->entnum != entnum )
continue;
if( ch->sfx != sfx )
continue;
return ch;
}
return NULL;
}
static void AL_AddLoopSounds( void ) {
int i;
int sounds[64]; // 64 is MAX_PACKET_ENTITIES in YQ2 (there's no define for it, though :/)
channel_t *ch;
sfx_t *sfx;
sfxcache_t *sc;
int num;
entity_state_t *ent;
if( cls.state != ca_active || cl_paused->value || !s_ambient->value ) {
return;
}
S_BuildSoundList( sounds );
for( i = 0; i < cl.frame.num_entities; i++ ) {
if (!sounds[i])
continue;
sfx = cl.sound_precache[sounds[i]];
if (!sfx)
continue; // bad sound effect
sc = sfx->cache;
if (!sc)
continue;
num = ( cl.frame.parse_entities + i ) & ( MAX_PARSE_ENTITIES - 1 );
ent = &cl_parse_entities [ num ];
ch = AL_FindLoopingSound( ent->number, sfx );
if( ch ) {
ch->autoframe = s_framecount;
ch->end = paintedtime + sc->length;
continue;
}
// allocate a channel
ch = S_PickChannel(0, 0);
if (!ch)
continue;
ch->autosound = true; // remove next frame
ch->autoframe = s_framecount;
ch->sfx = sfx;
ch->entnum = ent->number;
ch->master_vol = 1;
ch->dist_mult = SOUND_LOOPATTENUATE;
ch->end = paintedtime + sc->length;
AL_PlayChannel( ch );
}
}
static void AL_IssuePlaysounds( void ) {
playsound_t *ps;
// start any playsounds
while (1) {
ps = s_pendingplays.next;
if (ps == &s_pendingplays)
break; // no more pending sounds
if (ps->begin > paintedtime)
break;
S_IssuePlaysound (ps);
}
}
void AL_Update( void ) {
int i;
channel_t *ch;
vec_t orientation[6];
paintedtime = cl.time;
// set listener parameters
qalListener3f( AL_POSITION, AL_UnpackVector( listener_origin ) );
AL_CopyVector( listener_forward, orientation );
AL_CopyVector( listener_up, orientation + 3 );
qalListenerfv( AL_ORIENTATION, orientation );
qalListenerf( AL_GAIN, s_volume->value );
qalDistanceModel( AL_LINEAR_DISTANCE_CLAMPED );
// update spatialization for dynamic sounds
ch = channels;
for( i = 0; i < s_numchannels; i++, ch++ ) {
if( !ch->sfx )
continue;
if( ch->autosound ) {
// autosounds are regenerated fresh each frame
if( ch->autoframe != s_framecount ) {
AL_StopChannel( ch );
continue;
}
} else {
ALenum state;
qalGetError();
qalGetSourcei( ch->srcnum, AL_SOURCE_STATE, &state );
if( qalGetError() != AL_NO_ERROR || state == AL_STOPPED ) {
AL_StopChannel( ch );
continue;
}
}
if (s_show->value) {
Com_Printf ("%.1f %s\n", ch->master_vol, ch->sfx->name);
}
AL_Spatialize(ch); // respatialize channel
}
s_framecount++;
// add loopsounds
AL_AddLoopSounds ();
// add music
OGG_Stream();
S_AL_StreamUpdate();
AL_IssuePlaysounds();
}
void AL_Underwater()
{
int i;
// Apply to all sources
for (i = 0; i < s_numchannels; i++)
{
qalSourcei(s_srcnums[i], AL_DIRECT_FILTER, underwaterFilter);
}
}
void AL_Overwater()
{
int i;
// Apply to all sources
for (i = 0; i < s_numchannels; i++)
{
qalSourcei(s_srcnums[i], AL_DIRECT_FILTER, 0);
}
}
static void S_AL_StreamDie( void )
{
int numBuffers;
streamPlaying = false;
qalSourceStop(streamSource);
// Un-queue any buffers, and delete them
qalGetSourcei( streamSource, AL_BUFFERS_PROCESSED, &numBuffers );
while( numBuffers-- )
{
ALuint buffer;
qalSourceUnqueueBuffers(streamSource, 1, &buffer);
qalDeleteBuffers(1, &buffer);
active_buffers--;
}
}
static void S_AL_StreamUpdate( void )
{
int numBuffers;
ALint state;
// Un-queue any buffers, and delete them
qalGetSourcei( streamSource, AL_BUFFERS_PROCESSED, &numBuffers );
while( numBuffers-- )
{
ALuint buffer;
qalSourceUnqueueBuffers(streamSource, 1, &buffer);
qalDeleteBuffers(1, &buffer);
active_buffers--;
}
// Start the streamSource playing if necessary
qalGetSourcei( streamSource, AL_BUFFERS_QUEUED, &numBuffers );
qalGetSourcei(streamSource, AL_SOURCE_STATE, &state);
if(state == AL_STOPPED)
{
streamPlaying = false;
}
if( !streamPlaying && numBuffers )
{
qalSourcePlay( streamSource );
streamPlaying = true;
}
}
static ALuint S_AL_Format(int width, int channels)
{
ALuint format = AL_FORMAT_MONO16;
// Work out format
if(width == 1)
{
if(channels == 1)
format = AL_FORMAT_MONO8;
else if(channels == 2)
format = AL_FORMAT_STEREO8;
}
else if(width == 2)
{
if(channels == 1)
format = AL_FORMAT_MONO16;
else if(channels == 2)
format = AL_FORMAT_STEREO16;
}
return format;
}
void AL_RawSamples( int samples, int rate, int width, int channels, byte *data, float volume )
{
ALuint buffer;
ALuint format;
format = S_AL_Format( width, channels );
// Create a buffer, and stuff the data into it
qalGenBuffers(1, &buffer);
qalBufferData(buffer, format, (ALvoid *)data, (samples * width * channels), rate);
active_buffers++;
// set volume
qalSourcef( streamSource, AL_GAIN, volume );
// Shove the data onto the streamSource
qalSourceQueueBuffers(streamSource, 1, &buffer);
// emulate behavior of S_RawSamples for s_rawend
s_rawend += samples;
}
void AL_UnqueueRawSamples()
{
S_AL_StreamDie();
}
#endif // USE_OPENAL

View file

@ -27,12 +27,12 @@
*/
#include "../header/client.h"
#include "../../unix/header/qal.h"
#include "header/local.h"
#include "header/vorbis.h"
void S_Play ( void );
void S_SoundList ( void );
void S_Update_ ();
void S_StopAllSounds ( void );
/* only begin attenuating sound volumes when outside the FULLVOLUME range */
@ -42,9 +42,10 @@ void S_StopAllSounds ( void );
int s_registration_sequence;
channel_t channels [ MAX_CHANNELS ];
int s_numchannels;
qboolean snd_initialized = false;
int sound_started = 0;
sndstarted_t sound_started = SS_NOT;
dma_t dma;
@ -79,6 +80,7 @@ cvar_t *s_loadas8bit;
cvar_t *s_khz;
cvar_t *s_mixahead;
cvar_t *s_show;
cvar_t *s_ambient;
int s_rawend;
portable_samplepair_t s_rawsamples [ MAX_RAW_SAMPLES ];
@ -86,15 +88,8 @@ portable_samplepair_t s_rawsamples [ MAX_RAW_SAMPLES ];
/*
* User-setable variables
*/
void
S_SoundInfo_f ( void )
{
if ( !sound_started )
{
Com_Printf( "sound system not started\n" );
return;
}
static void DMA_SoundInfo (void) {
Com_Printf( "%5d stereo\n", dma.channels - 1 );
Com_Printf( "%5d samples\n", dma.samples );
Com_Printf( "%5d samplepos\n", dma.samplepos );
@ -104,6 +99,26 @@ S_SoundInfo_f ( void )
Com_Printf( "%p dma buffer\n", dma.buffer );
}
void
S_SoundInfo_f ( void )
{
if ( !sound_started )
{
Com_Printf( "sound system not started\n" );
return;
}
#if USE_OPENAL
if(sound_started == SS_OAL) {
QAL_SoundInfo();
Com_Printf("\nNumber of sources: %d\n", s_numchannels);
} else
#endif
DMA_SoundInfo();
}
void
S_Init ( void )
{
@ -116,16 +131,15 @@ S_Init ( void )
if ( !cv->value )
{
Com_Printf( "not initializing.\n" );
}
} else {
else
{
s_volume = Cvar_Get( "s_volume", "0.7", CVAR_ARCHIVE );
s_khz = Cvar_Get( "s_khz", "44", CVAR_ARCHIVE );
s_loadas8bit = Cvar_Get( "s_loadas8bit", "0", CVAR_ARCHIVE );
s_mixahead = Cvar_Get( "s_mixahead", "0.14", CVAR_ARCHIVE );
s_show = Cvar_Get( "s_show", "0", 0 );
s_testsound = Cvar_Get( "s_testsound", "0", 0 );
s_ambient = Cvar_Get( "s_ambient", "1", 0);
Cmd_AddCommand( "play", S_Play );
Cmd_AddCommand( "stopsound", S_StopAllSounds );
@ -136,20 +150,37 @@ S_Init ( void )
Cmd_AddCommand( "ogg_shutdown", OGG_Shutdown );
#endif
if ( !SNDDMA_Init() )
#if USE_OPENAL
cv = Cvar_Get( "s_openal", "1", CVAR_ARCHIVE);
if( cv->value && AL_Init() ) {
sound_started = SS_OAL;
}
else {
#endif
if ( SNDDMA_Init() )
{
sound_started = SS_DMA;
} else {
sound_started = SS_NOT;
return;
}
#if USE_OPENAL
} /* this is a bit ugly but prevents dangling else problems */
#endif
S_InitScaletable();
sound_started = 1;
num_sfx = 0;
soundtime = 0;
paintedtime = 0;
Com_Printf( "sound sampling rate: %i\n", dma.speed );
#if USE_OPENAL
if(sound_started == SS_DMA)
{
Com_Printf( "Sound sampling rate: %i\n", dma.speed );
}
#else
Com_Printf( "Sound sampling rate: %i\n", dma.speed );
#endif
S_StopAllSounds();
#ifdef OGG
@ -174,6 +205,8 @@ S_Shutdown ( void )
return;
}
S_StopAllSounds();
/* free all sounds */
for ( i = 0, sfx = known_sfx; i < num_sfx; i++, sfx++ )
{
@ -181,23 +214,41 @@ S_Shutdown ( void )
{
continue;
}
#if USE_OPENAL
if ( sound_started == SS_OAL )
{
AL_DeleteSfx( sfx );
}
#endif
if ( sfx->cache )
{
Z_Free( sfx->cache );
}
if ( sfx->truename )
{
Z_Free( sfx-> truename );
}
}
memset( known_sfx, 0, sizeof ( known_sfx ) );
num_sfx = 0;
sound_started = 0;
#ifdef OGG
OGG_Shutdown();
#endif
#if USE_OPENAL
if( sound_started == SS_OAL )
{
AL_Shutdown();
} else
#endif
SNDDMA_Shutdown();
sound_started = SS_NOT;
s_numchannels = 0;
Cmd_RemoveCommand( "soundlist" );
Cmd_RemoveCommand( "soundinfo" );
Cmd_RemoveCommand( "play" );
@ -407,7 +458,7 @@ S_PickChannel ( int entnum, int entchannel )
first_to_die = -1;
life_left = 0x7fffffff;
for ( ch_idx = 0; ch_idx < MAX_CHANNELS; ch_idx++ )
for ( ch_idx = 0; ch_idx < s_numchannels; ch_idx++ )
{
/* channel 0 never overrides */
if ( ( entchannel != 0 ) &&
@ -438,6 +489,13 @@ S_PickChannel ( int entnum, int entchannel )
}
ch = &channels [ first_to_die ];
#if USE_OPENAL
if( sound_started == SS_OAL && ch->sfx )
{
AL_StopChannel( ch );
}
#endif
memset( ch, 0, sizeof ( *ch ) );
return ( ch );
@ -593,6 +651,13 @@ S_IssuePlaysound ( playsound_t *ps )
return;
}
sc = S_LoadSound( ps->sfx );
if( !sc ) {
Com_Printf( "S_IssuePlaysound: couldn't load %s\n", ps->sfx->name );
S_FreePlaysound( ps );
return;
}
/* spatialize */
if ( ps->attenuation == ATTN_STATIC )
{
@ -611,16 +676,15 @@ S_IssuePlaysound ( playsound_t *ps )
VectorCopy( ps->origin, ch->origin );
ch->fixed_origin = ps->fixed_origin;
#if USE_OPENAL
if( sound_started == SS_OAL)
{
AL_PlayChannel( ch );
} else
#endif
S_Spatialize( ch );
ch->pos = 0;
sc = S_LoadSound( ch->sfx );
if (!sc)
{
return;
}
ch->end = paintedtime + sc->length;
/* free the playsound */
@ -690,6 +754,28 @@ S_RegisterSexedSound ( entity_state_t *ent, char *base )
return ( sfx );
}
static int DMA_DriftBeginofs( float timeofs ) {
/* drift s_beginofs */
int start = (int) ( cl.frame.servertime * 0.001f * dma.speed + s_beginofs );
if ( start < paintedtime )
{
start = paintedtime;
s_beginofs = (int) ( start - ( cl.frame.servertime * 0.001f * dma.speed ) );
}
else if ( start > paintedtime + 0.3f * dma.speed )
{
start = (int) ( paintedtime + 0.1f * dma.speed );
s_beginofs = (int) ( start - ( cl.frame.servertime * 0.001f * dma.speed ) );
}
else
{
s_beginofs -= 10;
}
return timeofs ? start + timeofs * dma.speed : paintedtime;
}
/*
* Validates the parms and ques the sound up if pos is NULL, the sound
* will be dynamically sourced from the entity Entchannel 0 will never
@ -699,9 +785,7 @@ void
S_StartSound ( vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol, float attenuation, float timeofs )
{
sfxcache_t *sc;
int vol;
playsound_t *ps, *sort;
int start;
if ( !sound_started )
{
@ -716,6 +800,9 @@ S_StartSound ( vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol
if ( sfx->name [ 0 ] == '*' )
{
sfx = S_RegisterSexedSound( &cl_entities [ entnum ].current, sfx->name );
if( !sfx ) {
return;
}
}
/* make sure the sound is loaded */
@ -726,8 +813,6 @@ S_StartSound ( vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol
return; /* couldn't load the sound's data */
}
vol = fvol * 255;
/* make the playsound_t */
ps = S_AllocPlaysound();
@ -749,37 +834,20 @@ S_StartSound ( vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol
ps->entnum = entnum;
ps->entchannel = entchannel;
ps->attenuation = attenuation;
ps->volume = vol;
ps->sfx = sfx;
/* drift s_beginofs */
start = (int) ( cl.frame.servertime * 0.001f * dma.speed + s_beginofs );
if ( start < paintedtime )
#if USE_OPENAL
if( sound_started == SS_OAL )
{
start = paintedtime;
s_beginofs = (int) ( start - ( cl.frame.servertime * 0.001f * dma.speed ) );
}
else if ( start > paintedtime + 0.3f * dma.speed )
{
start = (int) ( paintedtime + 0.1f * dma.speed );
s_beginofs = (int) ( start - ( cl.frame.servertime * 0.001f * dma.speed ) );
ps->begin = paintedtime + timeofs * 1000;
ps->volume = fvol * 384;
}
else
#endif
{
s_beginofs -= 10;
}
if ( !timeofs )
{
ps->begin = paintedtime;
}
else
{
ps->begin = (int) ( start + timeofs * dma.speed );
}
ps->begin = DMA_DriftBeginofs(timeofs);
ps->volume = fvol * 255;
}
/* sort into the pending sound list */
for ( sort = s_pendingplays.next;
@ -832,7 +900,6 @@ S_ClearBuffer ( void )
{
clear = 0x80;
}
else
{
clear = 0;
@ -880,10 +947,34 @@ S_StopAllSounds ( void )
s_playsounds [ i ].next->prev = &s_playsounds [ i ];
}
#if USE_OPENAL
if( sound_started == SS_OAL ) {
AL_StopAllChannels();
} else
#endif
S_ClearBuffer();
/* clear all the channels */
memset( channels, 0, sizeof ( channels ) );
}
S_ClearBuffer();
void S_BuildSoundList( int *sounds ) {
int i;
int num;
entity_state_t *ent;
for ( i = 0; i < cl.frame.num_entities; i++ )
{
num = ( cl.frame.parse_entities + i ) & ( MAX_PARSE_ENTITIES - 1 );
ent = &cl_parse_entities [ num ];
if( s_ambient->value == 2 && !ent->modelindex ) {
sounds[i] = 0;
} else if( s_ambient->value == 3 && ent->number != cl.playernum + 1) {
sounds[i] = 0;
} else {
sounds[i] = ent->sound;
}
}
}
/*
@ -914,17 +1005,12 @@ S_AddLoopSounds ( void )
return;
}
if ( !cl.sound_prepped )
if ( !cl.sound_prepped || !s_ambient->value )
{
return;
}
for ( i = 0; i < cl.frame.num_entities; i++ )
{
num = ( cl.frame.parse_entities + i ) & ( MAX_PARSE_ENTITIES - 1 );
ent = &cl_parse_entities [ num ];
sounds [ i ] = ent->sound;
}
S_BuildSoundList( sounds );
for ( i = 0; i < cl.frame.num_entities; i++ )
{
@ -1042,6 +1128,14 @@ S_RawSamples ( int samples, int rate, int width, int channels, byte *data, float
s_rawend = paintedtime;
}
#if USE_OPENAL
if( sound_started == SS_OAL )
{
AL_RawSamples(samples, rate, width, channels, data, volume);
return;
}
#endif
scale = (float) rate / dma.speed;
intVolume = (int) (256 * volume);
@ -1094,9 +1188,6 @@ S_RawSamples ( int samples, int rate, int width, int channels, byte *data, float
dst = s_rawend & ( MAX_RAW_SAMPLES - 1 );
s_rawend++;
// s_rawsamples [dst].left = ((char *) data)[src * 2] * intVolume;
// s_rawsamples [dst].right = ((char *) data)[src * 2 + 1] * intVolume;
/* the above doesn't work for me with U8, only the unsigned ones below do */
s_rawsamples [dst].left = (((byte *) data)[src * 2] - 128) * intVolume;
s_rawsamples [dst].right = (((byte *) data)[src * 2 + 1] - 128) * intVolume;
}
@ -1175,14 +1266,10 @@ S_Update ( vec3_t origin, vec3_t forward, vec3_t right, vec3_t up )
* dma buffer while loading */
if ( cls.disable_screen )
{
if (sound_started == SS_DMA ) {
S_ClearBuffer();
return;
}
/* rebuild scale tables if volume is modified */
if ( s_volume->modified )
{
S_InitScaletable();
return;
}
VectorCopy( origin, listener_origin );
@ -1190,10 +1277,24 @@ S_Update ( vec3_t origin, vec3_t forward, vec3_t right, vec3_t up )
VectorCopy( right, listener_right );
VectorCopy( up, listener_up );
#if USE_OPENAL
if( sound_started == SS_OAL ) {
AL_Update();
return;
}
#endif
/* rebuild scale tables if volume is modified */
if ( s_volume->modified )
{
S_InitScaletable();
}
/* update spatialization for dynamic sounds */
ch = channels;
for ( i = 0; i < MAX_CHANNELS; i++, ch++ )
for ( i = 0; i < s_numchannels; i++, ch++ )
{
if ( !ch->sfx )
{
@ -1225,7 +1326,7 @@ S_Update ( vec3_t origin, vec3_t forward, vec3_t right, vec3_t up )
total = 0;
ch = channels;
for ( i = 0; i < MAX_CHANNELS; i++, ch++ )
for ( i = 0; i < s_numchannels; i++, ch++ )
{
if ( ch->sfx && ( ch->leftvol || ch->rightvol ) )
{

View file

@ -149,10 +149,7 @@ S_LoadSound ( sfx_t *s )
if ( name [ 0 ] == '#' )
{
strcpy( namebuffer, &name [ 1 ] );
}
else
{
} else {
Com_sprintf( namebuffer, sizeof ( namebuffer ), "sound/%s", name );
}
@ -173,7 +170,7 @@ S_LoadSound ( sfx_t *s )
FS_FreeFile( data );
return ( NULL );
}
if (sound_started != SS_OAL) {
stepscale = (float) info.rate / dma.speed;
len = (int) ( info.samples / stepscale );
@ -198,7 +195,13 @@ S_LoadSound ( sfx_t *s )
sc->speed = info.rate;
sc->width = info.width;
sc->stereo = info.channels;
}
#if USE_OPENAL
if (sound_started == SS_OAL)
sc = AL_UploadSfx(s, &info, data + info.dataofs);
else
#endif
ResampleSfx( s, sc->speed, sc->width, data + info.dataofs );
FS_FreeFile( data );

View file

@ -202,12 +202,6 @@ S_PaintChannels ( int endtime )
end = paintedtime + PAINTBUFFER_SIZE;
}
/* start any playsounds */
if ( endtime - paintedtime > PAINTBUFFER_SIZE )
{
end = paintedtime + PAINTBUFFER_SIZE;
}
/* start any playsounds */
for ( ; ; )
{
@ -258,6 +252,7 @@ S_PaintChannels ( int endtime )
for ( ; i < end; i++ )
{
// TODO: this could be done with memset
paintbuffer [ i - paintedtime ].left = paintbuffer [ i - paintedtime ].right = 0;
}
}
@ -265,7 +260,7 @@ S_PaintChannels ( int endtime )
/* paint in the channels. */
ch = channels;
for ( i = 0; i < MAX_CHANNELS; i++, ch++ )
for ( i = 0; i < s_numchannels; i++, ch++ )
{
ltime = paintedtime;

View file

@ -39,8 +39,9 @@
#include "header/local.h"
#include "header/vorbis.h"
extern int sound_started; /* Sound initialization flag. */
extern cvar_t *fs_basedir; /* Path to "music". */
#ifdef USE_OPENAL
void AL_UnqueueRawSamples();
#endif
qboolean ogg_first_init = true; /* First initialization flag. */
qboolean ogg_started = false; /* Initialization flag. */
@ -59,6 +60,7 @@ cvar_t *ogg_sequence; /* Sequence play indicator. */
cvar_t *ogg_volume; /* Music volume. */
OggVorbis_File ovFile; /* Ogg Vorbis file. */
vorbis_info *ogg_info; /* Ogg Vorbis file information */
int ogg_numbufs; /* Number of buffers for OpenAL */
/*
* Initialize the Ogg Vorbis subsystem.
@ -585,9 +587,14 @@ OGG_Stop ( void )
return;
}
#ifdef USE_OPENAL
AL_UnqueueRawSamples();
#endif
ov_clear( &ovFile );
ogg_status = STOP;
ogg_info = NULL;
ogg_numbufs = 0;
if ( ogg_buffer != NULL )
{
@ -607,10 +614,50 @@ OGG_Stream ( void )
return;
}
while ( ogg_status == PLAY && paintedtime + MAX_RAW_SAMPLES - 2048 > s_rawend )
if ( ogg_status == PLAY )
{
#ifdef USE_OPENAL
if ( sound_started == SS_OAL )
{
/* Calculate the number of buffers used
for storing decoded OGG/Vorbis data.
We take the number of active buffers
at startup (at this point most of the
samples should be precached and loaded
into buffers) and add 64. Empircal
testing showed, that at most times
at least 52 buffers remain available
for OGG/Vorbis, enough for about 3
seconds playback. The music won't
stutter as long as the framerate
stayes over 1 FPS. */
if ( ogg_numbufs == 0 )
{
ogg_numbufs = active_buffers + 64;
}
/* active_buffers are all active OpenAL buffers,
buffering normal sfx _and_ ogg/vorbis samples. */
while ( active_buffers <= ogg_numbufs )
{
OGG_Read();
}
} else { /* using DMA/SDL */
#endif
/* Read that number samples into the buffer, that
were played since the last call to this function.
This keeps the buffer at all times at an "optimal"
fill level. */
while ( paintedtime + MAX_RAW_SAMPLES - 2048 > s_rawend )
{
OGG_Read();
}
#ifdef USE_OPENAL
} /* using DMA/SDL */
#endif
} /* ogg_status == PLAY */
}
/*
@ -695,6 +742,7 @@ OGG_PauseCmd ( void )
if ( ogg_status == PLAY )
{
ogg_status = PAUSE;
ogg_numbufs = 0;
}
}

View file

@ -27,6 +27,9 @@
#include "../header/client.h"
#include "header/local.h"
// FIXME: this code is really fucked up, those global variables make me sick.
// someone should clean this up one day..
byte *data_p;
byte *iff_end;
byte *last_chunk;

View file

@ -27,6 +27,11 @@
#include "header/common.h"
#if !defined(DEDICATED_ONLY) && defined(USE_OPENAL)
void AL_Underwater();
void AL_Overwater();
#endif
#define STEPSIZE 18
/* all of the locals will be zeroed before each
@ -1108,6 +1113,10 @@ void PM_ClampAngles (void)
*/
void Pmove (pmove_t *pmove)
{
#if !defined(DEDICATED_ONLY) && defined(USE_OPENAL)
static int underwater;
#endif
pm = pmove;
/* clear results */
@ -1235,5 +1244,19 @@ void Pmove (pmove_t *pmove)
/* set groundentity, watertype, and waterlevel for final spot */
PM_CatagorizePosition ();
#if !defined(DEDICATED_ONLY) && defined(USE_OPENAL)
if ((pm->waterlevel == 3) && !underwater)
{
underwater = 1;
AL_Underwater();
}
if ((pm->waterlevel < 3 && underwater))
{
underwater = 0;
AL_Overwater();
}
#endif
PM_SnapPosition ();
}

View file

@ -208,6 +208,9 @@ SNDDMA_Init(void)
dmasize = (dmabackend->samples * (dmabackend->samplebits / 8));
dmabackend->buffer = calloc(1, dmasize);
s_numchannels = MAX_CHANNELS;
S_InitScaletable();
SDL_PauseAudio(0);
Com_Printf("SDL audio initialized.\n");

137
src/unix/header/qal.h Normal file
View file

@ -0,0 +1,137 @@
/*
* Copyright (C) 2012 Yamagi Burmeister
* Copyright (C) 2010 skuller.net
*
* This program 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 2 of the License, or (at
* your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*
* =======================================================================
*
* Header file to the low level "qal" API implementation. This source file
* was taken from Q2Pro and modified by the YQ2 authors.
*
* =======================================================================
*/
#ifdef USE_OPENAL
#ifndef _QAL_API_H_
#define _QAL_API_H_
#include <AL/al.h>
#include <AL/efx.h>
/* Function pointers used to tie
the qal API to the OpenAL API */
extern LPALENABLE qalEnable;
extern LPALDISABLE qalDisable;
extern LPALISENABLED qalIsEnabled;
extern LPALGETSTRING qalGetString;
extern LPALGETBOOLEANV qalGetBooleanv;
extern LPALGETINTEGERV qalGetIntegerv;
extern LPALGETFLOATV qalGetFloatv;
extern LPALGETDOUBLEV qalGetDoublev;
extern LPALGETBOOLEAN qalGetBoolean;
extern LPALGETINTEGER qalGetInteger;
extern LPALGETFLOAT qalGetFloat;
extern LPALGETDOUBLE qalGetDouble;
extern LPALGETERROR qalGetError;
extern LPALISEXTENSIONPRESENT qalIsExtensionPresent;
extern LPALGETPROCADDRESS qalGetProcAddress;
extern LPALGETENUMVALUE qalGetEnumValue;
extern LPALLISTENERF qalListenerf;
extern LPALLISTENER3F qalListener3f;
extern LPALLISTENERFV qalListenerfv;
extern LPALLISTENERI qalListeneri;
extern LPALLISTENER3I qalListener3i;
extern LPALLISTENERIV qalListeneriv;
extern LPALGETLISTENERF qalGetListenerf;
extern LPALGETLISTENER3F qalGetListener3f;
extern LPALGETLISTENERFV qalGetListenerfv;
extern LPALGETLISTENERI qalGetListeneri;
extern LPALGETLISTENER3I qalGetListener3i;
extern LPALGETLISTENERIV qalGetListeneriv;
extern LPALGENSOURCES qalGenSources;
extern LPALDELETESOURCES qalDeleteSources;
extern LPALISSOURCE qalIsSource;
extern LPALSOURCEF qalSourcef;
extern LPALSOURCE3F qalSource3f;
extern LPALSOURCEFV qalSourcefv;
extern LPALSOURCEI qalSourcei;
extern LPALSOURCE3I qalSource3i;
extern LPALSOURCEIV qalSourceiv;
extern LPALGETSOURCEF qalGetSourcef;
extern LPALGETSOURCE3F qalGetSource3f;
extern LPALGETSOURCEFV qalGetSourcefv;
extern LPALGETSOURCEI qalGetSourcei;
extern LPALGETSOURCE3I qalGetSource3i;
extern LPALGETSOURCEIV qalGetSourceiv;
extern LPALSOURCEPLAYV qalSourcePlayv;
extern LPALSOURCESTOPV qalSourceStopv;
extern LPALSOURCEREWINDV qalSourceRewindv;
extern LPALSOURCEPAUSEV qalSourcePausev;
extern LPALSOURCEPLAY qalSourcePlay;
extern LPALSOURCESTOP qalSourceStop;
extern LPALSOURCEREWIND qalSourceRewind;
extern LPALSOURCEPAUSE qalSourcePause;
extern LPALSOURCEQUEUEBUFFERS qalSourceQueueBuffers;
extern LPALSOURCEUNQUEUEBUFFERS qalSourceUnqueueBuffers;
extern LPALGENBUFFERS qalGenBuffers;
extern LPALDELETEBUFFERS qalDeleteBuffers;
extern LPALISBUFFER qalIsBuffer;
extern LPALBUFFERDATA qalBufferData;
extern LPALBUFFERF qalBufferf;
extern LPALBUFFER3F qalBuffer3f;
extern LPALBUFFERFV qalBufferfv;
extern LPALBUFFERI qalBufferi;
extern LPALBUFFER3I qalBuffer3i;
extern LPALBUFFERIV qalBufferiv;
extern LPALGETBUFFERF qalGetBufferf;
extern LPALGETBUFFER3F qalGetBuffer3f;
extern LPALGETBUFFERFV qalGetBufferfv;
extern LPALGETBUFFERI qalGetBufferi;
extern LPALGETBUFFER3I qalGetBuffer3i;
extern LPALGETBUFFERIV qalGetBufferiv;
extern LPALDOPPLERFACTOR qalDopplerFactor;
extern LPALDOPPLERVELOCITY qalDopplerVelocity;
extern LPALSPEEDOFSOUND qalSpeedOfSound;
extern LPALDISTANCEMODEL qalDistanceModel;
extern LPALGENFILTERS qalGenFilters;
extern LPALFILTERI qalFilteri;
extern LPALFILTERF qalFilterf;
extern LPALDELETEFILTERS qalDeleteFilters;
/*
* Gives information over the OpenAL
* implementation and it's state
*/
void QAL_SoundInfo(void);
/*
* Loads the OpenAL shared lib, creates
* a context and device handle.
*/
qboolean QAL_Init(void);
/*
* Shuts OpenAL down, frees all context and
* device handles and unloads the shared lib.
*/
void QAL_Shutdown(void);
#endif /* _QAL_API_H_ */
#endif /* USE_OPENAL */

501
src/unix/qal.c Normal file
View file

@ -0,0 +1,501 @@
/*
* Copyright (C) 2012 Yamagi Burmeister
* Copyright (C) 2010 skuller.net
*
* This program 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 2 of the License, or (at
* your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*
* =======================================================================
*
* Low level, platform depended "qal" API implementation. This files
* provides functions to load, initialize, shutdown und unload the
* OpenAL library and connects the "qal" funtion pointers to the
* OpenAL functions. This source file was taken from Q2Pro and
* modified by the YQ2 authors.
*
* =======================================================================
*/
#ifdef USE_OPENAL
#include <dlfcn.h>
#include <AL/al.h>
#include <AL/alc.h>
#include <AL/alext.h>
#include "../common/header/common.h"
#include "header/qal.h"
static ALCcontext *context;
static ALCdevice *device;
static cvar_t *al_device;
static cvar_t *al_driver;
static void *handle;
/* Function pointers for OpenAL management */
static LPALCCREATECONTEXT qalcCreateContext;
static LPALCMAKECONTEXTCURRENT qalcMakeContextCurrent;
static LPALCPROCESSCONTEXT qalcProcessContext;
static LPALCSUSPENDCONTEXT qalcSuspendContext;
static LPALCDESTROYCONTEXT qalcDestroyContext;
static LPALCGETCURRENTCONTEXT qalcGetCurrentContext;
static LPALCGETCONTEXTSDEVICE qalcGetContextsDevice;
static LPALCOPENDEVICE qalcOpenDevice;
static LPALCCLOSEDEVICE qalcCloseDevice;
static LPALCGETERROR qalcGetError;
static LPALCISEXTENSIONPRESENT qalcIsExtensionPresent;
static LPALCGETPROCADDRESS qalcGetProcAddress;
static LPALCGETENUMVALUE qalcGetEnumValue;
static LPALCGETSTRING qalcGetString;
static LPALCGETINTEGERV qalcGetIntegerv;
static LPALCCAPTUREOPENDEVICE qalcCaptureOpenDevice;
static LPALCCAPTURECLOSEDEVICE qalcCaptureCloseDevice;
static LPALCCAPTURESTART qalcCaptureStart;
static LPALCCAPTURESTOP qalcCaptureStop;
static LPALCCAPTURESAMPLES qalcCaptureSamples ;
/* Declaration of function pointers used
to connect OpenAL to our internal API */
LPALENABLE qalEnable;
LPALDISABLE qalDisable;
LPALISENABLED qalIsEnabled;
LPALGETSTRING qalGetString;
LPALGETBOOLEANV qalGetBooleanv;
LPALGETINTEGERV qalGetIntegerv;
LPALGETFLOATV qalGetFloatv;
LPALGETDOUBLEV qalGetDoublev;
LPALGETBOOLEAN qalGetBoolean;
LPALGETINTEGER qalGetInteger;
LPALGETFLOAT qalGetFloat;
LPALGETDOUBLE qalGetDouble;
LPALGETERROR qalGetError;
LPALISEXTENSIONPRESENT qalIsExtensionPresent;
LPALGETPROCADDRESS qalGetProcAddress;
LPALGETENUMVALUE qalGetEnumValue;
LPALLISTENERF qalListenerf;
LPALLISTENER3F qalListener3f;
LPALLISTENERFV qalListenerfv;
LPALLISTENERI qalListeneri;
LPALLISTENER3I qalListener3i;
LPALLISTENERIV qalListeneriv;
LPALGETLISTENERF qalGetListenerf;
LPALGETLISTENER3F qalGetListener3f;
LPALGETLISTENERFV qalGetListenerfv;
LPALGETLISTENERI qalGetListeneri;
LPALGETLISTENER3I qalGetListener3i;
LPALGETLISTENERIV qalGetListeneriv;
LPALGENSOURCES qalGenSources;
LPALDELETESOURCES qalDeleteSources;
LPALISSOURCE qalIsSource;
LPALSOURCEF qalSourcef;
LPALSOURCE3F qalSource3f;
LPALSOURCEFV qalSourcefv;
LPALSOURCEI qalSourcei;
LPALSOURCE3I qalSource3i;
LPALSOURCEIV qalSourceiv;
LPALGETSOURCEF qalGetSourcef;
LPALGETSOURCE3F qalGetSource3f;
LPALGETSOURCEFV qalGetSourcefv;
LPALGETSOURCEI qalGetSourcei;
LPALGETSOURCE3I qalGetSource3i;
LPALGETSOURCEIV qalGetSourceiv;
LPALSOURCEPLAYV qalSourcePlayv;
LPALSOURCESTOPV qalSourceStopv;
LPALSOURCEREWINDV qalSourceRewindv;
LPALSOURCEPAUSEV qalSourcePausev;
LPALSOURCEPLAY qalSourcePlay;
LPALSOURCESTOP qalSourceStop;
LPALSOURCEREWIND qalSourceRewind;
LPALSOURCEPAUSE qalSourcePause;
LPALSOURCEQUEUEBUFFERS qalSourceQueueBuffers;
LPALSOURCEUNQUEUEBUFFERS qalSourceUnqueueBuffers;
LPALGENBUFFERS qalGenBuffers;
LPALDELETEBUFFERS qalDeleteBuffers;
LPALISBUFFER qalIsBuffer;
LPALBUFFERDATA qalBufferData;
LPALBUFFERF qalBufferf;
LPALBUFFER3F qalBuffer3f;
LPALBUFFERFV qalBufferfv;
LPALBUFFERI qalBufferi;
LPALBUFFER3I qalBuffer3i;
LPALBUFFERIV qalBufferiv;
LPALGETBUFFERF qalGetBufferf;
LPALGETBUFFER3F qalGetBuffer3f;
LPALGETBUFFERFV qalGetBufferfv;
LPALGETBUFFERI qalGetBufferi;
LPALGETBUFFER3I qalGetBuffer3i;
LPALGETBUFFERIV qalGetBufferiv;
LPALDOPPLERFACTOR qalDopplerFactor;
LPALDOPPLERVELOCITY qalDopplerVelocity;
LPALSPEEDOFSOUND qalSpeedOfSound;
LPALDISTANCEMODEL qalDistanceModel;
LPALGENFILTERS qalGenFilters;
LPALFILTERI qalFilteri;
LPALFILTERF qalFilterf;
LPALDELETEFILTERS qalDeleteFilters;
/*
* Gives information over the OpenAL
* implementation and it's state
*/
void QAL_SoundInfo()
{
Com_Printf("OpenAL settings:\n");
Com_Printf("AL_VENDOR: %s\n", qalGetString(AL_VENDOR));
Com_Printf("AL_RENDERER: %s\n", qalGetString(AL_RENDERER));
Com_Printf("AL_VERSION: %s\n", qalGetString(AL_VERSION));
Com_Printf("AL_EXTENSIONS: %s\n", qalGetString(AL_EXTENSIONS));
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT"))
{
const char *devs = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
Com_Printf("\nAvailable OpenAL devices:\n");
if (devs == NULL)
{
Com_Printf("- No devices found. Depending on your\n");
Com_Printf(" platform this may be expected and\n");
Com_Printf(" doesn't indicate a problem!\n");
}
else
{
while (devs && *devs)
{
Com_Printf("- %s\n", devs);
devs += strlen(devs) + 1;
}
}
}
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT"))
{
const char *devs = alcGetString(device, ALC_DEVICE_SPECIFIER);
Com_Printf("\nCurrent OpenAL device:\n");
if (devs == NULL)
{
Com_Printf("- No OpenAL device in use\n");
}
else
{
Com_Printf("- %s\n", devs);
}
}
}
/*
* Shuts OpenAL down, frees all context and
* device handles and unloads the shared lib.
*/
void
QAL_Shutdown()
{
if (context)
{
qalcMakeContextCurrent( NULL );
qalcDestroyContext( context );
context = NULL;
}
if (device)
{
qalcCloseDevice( device );
device = NULL;
}
/* Disconnect function pointers used
for OpenAL management calls */
qalcCreateContext = NULL;
qalcMakeContextCurrent = NULL;
qalcProcessContext = NULL;
qalcSuspendContext = NULL;
qalcDestroyContext = NULL;
qalcGetCurrentContext = NULL;
qalcGetContextsDevice = NULL;
qalcOpenDevice = NULL;
qalcCloseDevice = NULL;
qalcGetError = NULL;
qalcIsExtensionPresent = NULL;
qalcGetProcAddress = NULL;
qalcGetEnumValue = NULL;
qalcGetString = NULL;
qalcGetIntegerv = NULL;
qalcCaptureOpenDevice = NULL;
qalcCaptureCloseDevice = NULL;
qalcCaptureStart = NULL;
qalcCaptureStop = NULL;
qalcCaptureSamples = NULL;
/* Disconnect OpenAL
* function pointers */
qalEnable = NULL;
qalDisable = NULL;
qalIsEnabled = NULL;
qalGetString = NULL;
qalGetBooleanv = NULL;
qalGetIntegerv = NULL;
qalGetFloatv = NULL;
qalGetDoublev = NULL;
qalGetBoolean = NULL;
qalGetInteger = NULL;
qalGetFloat = NULL;
qalGetDouble = NULL;
qalGetError = NULL;
qalIsExtensionPresent = NULL;
qalGetProcAddress = NULL;
qalGetEnumValue = NULL;
qalListenerf = NULL;
qalListener3f = NULL;
qalListenerfv = NULL;
qalListeneri = NULL;
qalListener3i = NULL;
qalListeneriv = NULL;
qalGetListenerf = NULL;
qalGetListener3f = NULL;
qalGetListenerfv = NULL;
qalGetListeneri = NULL;
qalGetListener3i = NULL;
qalGetListeneriv = NULL;
qalGenSources = NULL;
qalDeleteSources = NULL;
qalIsSource = NULL;
qalSourcef = NULL;
qalSource3f = NULL;
qalSourcefv = NULL;
qalSourcei = NULL;
qalSource3i = NULL;
qalSourceiv = NULL;
qalGetSourcef = NULL;
qalGetSource3f = NULL;
qalGetSourcefv = NULL;
qalGetSourcei = NULL;
qalGetSource3i = NULL;
qalGetSourceiv = NULL;
qalSourcePlayv = NULL;
qalSourceStopv = NULL;
qalSourceRewindv = NULL;
qalSourcePausev = NULL;
qalSourcePlay = NULL;
qalSourceStop = NULL;
qalSourceRewind = NULL;
qalSourcePause = NULL;
qalSourceQueueBuffers = NULL;
qalSourceUnqueueBuffers = NULL;
qalGenBuffers = NULL;
qalDeleteBuffers = NULL;
qalIsBuffer = NULL;
qalBufferData = NULL;
qalBufferf = NULL;
qalBuffer3f = NULL;
qalBufferfv = NULL;
qalBufferi = NULL;
qalBuffer3i = NULL;
qalBufferiv = NULL;
qalGetBufferf = NULL;
qalGetBuffer3f = NULL;
qalGetBufferfv = NULL;
qalGetBufferi = NULL;
qalGetBuffer3i = NULL;
qalGetBufferiv = NULL;
qalDopplerFactor = NULL;
qalDopplerVelocity = NULL;
qalSpeedOfSound = NULL;
qalDistanceModel = NULL;
qalGenFilters = NULL;
qalFilteri = NULL;
qalFilterf = NULL;
qalDeleteFilters = NULL;
/* Unload the shared lib */
dlclose(handle);
handle = NULL;
}
/*
* Loads the OpenAL shared lib, creates
* a context and device handle.
*/
qboolean
QAL_Init()
{
/* DEFAULT_OPENAL_DRIVER is defined at compile time via the compiler */
al_driver = Cvar_Get( "al_driver", DEFAULT_OPENAL_DRIVER, CVAR_ARCHIVE );
al_device = Cvar_Get( "al_device", "", CVAR_ARCHIVE );
Com_Printf("LoadLibrary(%s)\n", al_driver->string);
/* Load the library */
handle = dlopen( al_driver->string, RTLD_LAZY );
if (!handle)
{
return false;
}
/* Connect function pointers to management functions */
qalcCreateContext = dlsym(handle, "alcCreateContext");
qalcMakeContextCurrent = dlsym(handle, "alcMakeContextCurrent");
qalcProcessContext = dlsym(handle, "alcProcessContext");
qalcSuspendContext = dlsym(handle, "alcSuspendContext");
qalcDestroyContext = dlsym(handle, "alcDestroyContext");
qalcGetCurrentContext = dlsym(handle, "alcGetCurrentContext");
qalcGetContextsDevice = dlsym(handle, "alcGetContextsDevice");
qalcOpenDevice = dlsym(handle, "alcOpenDevice");
qalcCloseDevice = dlsym(handle, "alcCloseDevice");
qalcGetError = dlsym(handle, "alcGetError");
qalcIsExtensionPresent = dlsym(handle, "alcIsExtensionPresent");
qalcGetProcAddress = dlsym(handle, "alcGetProcAddress");
qalcGetEnumValue = dlsym(handle, "alcGetEnumValue");
qalcGetString = dlsym(handle, "alcGetString");
qalcGetIntegerv = dlsym(handle, "alcGetIntegerv");
qalcCaptureOpenDevice = dlsym(handle, "alcCaptureOpenDevice");
qalcCaptureCloseDevice = dlsym(handle, "alcCaptureCloseDevice");
qalcCaptureStart = dlsym(handle, "alcCaptureStart");
qalcCaptureStop = dlsym(handle, "alcCaptureStop");
qalcCaptureSamples = dlsym(handle, "alcCaptureSamples");
/* Connect function pointers to
to OpenAL API functions */
qalEnable = dlsym(handle, "alEnable");
qalDisable = dlsym(handle, "alDisable");
qalIsEnabled = dlsym(handle, "alIsEnabled");
qalGetString = dlsym(handle, "alGetString");
qalGetBooleanv = dlsym(handle, "alGetBooleanv");
qalGetIntegerv = dlsym(handle, "alGetIntegerv");
qalGetFloatv = dlsym(handle, "alGetFloatv");
qalGetDoublev = dlsym(handle, "alGetDoublev");
qalGetBoolean = dlsym(handle, "alGetBoolean");
qalGetInteger = dlsym(handle, "alGetInteger");
qalGetFloat = dlsym(handle, "alGetFloat");
qalGetDouble = dlsym(handle, "alGetDouble");
qalGetError = dlsym(handle, "alGetError");
qalIsExtensionPresent = dlsym(handle, "alIsExtensionPresent");
qalGetProcAddress = dlsym(handle, "alGetProcAddress");
qalGetEnumValue = dlsym(handle, "alGetEnumValue");
qalListenerf = dlsym(handle, "alListenerf");
qalListener3f = dlsym(handle, "alListener3f");
qalListenerfv = dlsym(handle, "alListenerfv");
qalListeneri = dlsym(handle, "alListeneri");
qalListener3i = dlsym(handle, "alListener3i");
qalListeneriv = dlsym(handle, "alListeneriv");
qalGetListenerf = dlsym(handle, "alGetListenerf");
qalGetListener3f = dlsym(handle, "alGetListener3f");
qalGetListenerfv = dlsym(handle, "alGetListenerfv");
qalGetListeneri = dlsym(handle, "alGetListeneri");
qalGetListener3i = dlsym(handle, "alGetListener3i");
qalGetListeneriv = dlsym(handle, "alGetListeneriv");
qalGenSources = dlsym(handle, "alGenSources");
qalDeleteSources = dlsym(handle, "alDeleteSources");
qalIsSource = dlsym(handle, "alIsSource");
qalSourcef = dlsym(handle, "alSourcef");
qalSource3f = dlsym(handle, "alSource3f");
qalSourcefv = dlsym(handle, "alSourcefv");
qalSourcei = dlsym(handle, "alSourcei");
qalSource3i = dlsym(handle, "alSource3i");
qalSourceiv = dlsym(handle, "alSourceiv");
qalGetSourcef = dlsym(handle, "alGetSourcef");
qalGetSource3f = dlsym(handle, "alGetSource3f");
qalGetSourcefv = dlsym(handle, "alGetSourcefv");
qalGetSourcei = dlsym(handle, "alGetSourcei");
qalGetSource3i = dlsym(handle, "alGetSource3i");
qalGetSourceiv = dlsym(handle, "alGetSourceiv");
qalSourcePlayv = dlsym(handle, "alSourcePlayv");
qalSourceStopv = dlsym(handle, "alSourceStopv");
qalSourceRewindv = dlsym(handle, "alSourceRewindv");
qalSourcePausev = dlsym(handle, "alSourcePausev");
qalSourcePlay = dlsym(handle, "alSourcePlay");
qalSourceStop = dlsym(handle, "alSourceStop");
qalSourceRewind = dlsym(handle, "alSourceRewind");
qalSourcePause = dlsym(handle, "alSourcePause");
qalSourceQueueBuffers = dlsym(handle, "alSourceQueueBuffers");
qalSourceUnqueueBuffers = dlsym(handle, "alSourceUnqueueBuffers");
qalGenBuffers = dlsym(handle, "alGenBuffers");
qalDeleteBuffers = dlsym(handle, "alDeleteBuffers");
qalIsBuffer = dlsym(handle, "alIsBuffer");
qalBufferData = dlsym(handle, "alBufferData");
qalBufferf = dlsym(handle, "alBufferf");
qalBuffer3f = dlsym(handle, "alBuffer3f");
qalBufferfv = dlsym(handle, "alBufferfv");
qalBufferi = dlsym(handle, "alBufferi");
qalBuffer3i = dlsym(handle, "alBuffer3i");
qalBufferiv = dlsym(handle, "alBufferiv");
qalGetBufferf = dlsym(handle, "alGetBufferf");
qalGetBuffer3f = dlsym(handle, "alGetBuffer3f");
qalGetBufferfv = dlsym(handle, "alGetBufferfv");
qalGetBufferi = dlsym(handle, "alGetBufferi");
qalGetBuffer3i = dlsym(handle, "alGetBuffer3i");
qalGetBufferiv = dlsym(handle, "alGetBufferiv");
qalDopplerFactor = dlsym(handle, "alDopplerFactor");
qalDopplerVelocity = dlsym(handle, "alDopplerVelocity");
qalSpeedOfSound = dlsym(handle, "alSpeedOfSound");
qalDistanceModel = dlsym(handle, "alDistanceModel");
qalGenFilters = dlsym(handle, "alGenFilters");
qalFilteri = dlsym(handle, "alFilteri");
qalFilterf = dlsym(handle, "alFilterf");
qalDeleteFilters = dlsym(handle, "alDeleteFilters");
/* Open the OpenAL device */
Com_Printf("...opening OpenAL device:");
device = qalcOpenDevice(al_device->string[0] ? al_device->string : NULL);
if(!device)
{
Com_DPrintf("failed\n");
QAL_Shutdown();
return false;
}
Com_Printf("ok\n");
/* Create the OpenAL context */
Com_Printf("...creating OpenAL context: ");
context = qalcCreateContext(device, NULL);
if(!context)
{
Com_DPrintf("failed\n");
QAL_Shutdown();
return false;
}
Com_Printf("ok\n");
/* Set the created context as current context */
Com_Printf("...making context current: ");
if (!qalcMakeContextCurrent(context))
{
Com_DPrintf("failed\n");
QAL_Shutdown();
return false;
}
Com_Printf("ok\n");
/* Print OpenAL informations */
Com_Printf("\n");
QAL_SoundInfo();
Com_Printf("\n");
return true;
}
#endif /* USE_OPENAL */