#include "quakedef.h"

/*
This is based on Jogi's OpenAL support.
Much of it is stripped, to try and get it clean/compliant.

Missing features:
FIXME: does not kill old sounds on replaced sound channels (quake compliance).
FIXME: no static/ambient sounds (quake compliance).
FIXME: does not support streaming audio from voice/videos (voice/q2/q3 compliance).
If the above ar implemented, it can be the default device.
FIXME: listener velocity calculations (currently ugly).
FIXME: does not track entity velocities, so no dopler (awkward, quake move playing sounds at all).
FIXME: no eax (underwater).
FIXME: a capture device would be useful (voice chat).
*/

#ifdef AVAIL_OPENAL

#if defined(_WIN32)
 #define AL_APIENTRY __cdecl
#else
 #define AL_APIENTRY
#endif
#define AL_API


typedef int ALint;
typedef unsigned int ALuint;
typedef float ALfloat;
typedef int ALenum;
typedef char ALchar;
typedef char ALboolean;
typedef int ALsizei;
typedef void ALvoid;

static AL_API ALenum (AL_APIENTRY *palGetError)( void );
static AL_API void (AL_APIENTRY *palSourcef)( ALuint sid, ALenum param, ALfloat value ); 
static AL_API void (AL_APIENTRY *palSourcei)( ALuint sid, ALenum param, ALint value ); 

static AL_API void (AL_APIENTRY *palSourcePlayv)( ALsizei ns, const ALuint *sids );
static AL_API void (AL_APIENTRY *palSourceStopv)( ALsizei ns, const ALuint *sids );
static AL_API void (AL_APIENTRY *palSourcePlay)( ALuint sid );
static AL_API void (AL_APIENTRY *palSourceStop)( ALuint sid );

static AL_API void (AL_APIENTRY *palDopplerFactor)( ALfloat value );

static AL_API void (AL_APIENTRY *palGenBuffers)( ALsizei n, ALuint* buffers );
static AL_API ALboolean (AL_APIENTRY *palIsBuffer)( ALuint bid );
static AL_API void (AL_APIENTRY *palBufferData)( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq );
static AL_API void (AL_APIENTRY *palDeleteBuffers)( ALsizei n, const ALuint* buffers );

static AL_API void (AL_APIENTRY *palListenerfv)( ALenum param, const ALfloat* values ); 
static AL_API void (AL_APIENTRY *palSourcefv)( ALuint sid, ALenum param, const ALfloat* values ); 
static AL_API const ALchar* (AL_APIENTRY *palGetString)( ALenum param );
static AL_API void (AL_APIENTRY *palGenSources)( ALsizei n, ALuint* sources ); 
static AL_API void (AL_APIENTRY *palListenerf)( ALenum param, ALfloat value );
static AL_API void (AL_APIENTRY *palDeleteBuffers)( ALsizei n, const ALuint* buffers );
static AL_API void (AL_APIENTRY *palDeleteSources)( ALsizei n, const ALuint* sources );
static AL_API void (AL_APIENTRY *palSpeedOfSound)( ALfloat value );
static AL_API void (AL_APIENTRY *palDistanceModel)( ALenum distanceModel );


#define AL_NONE                                   0
#define AL_FALSE                                  0
#define AL_TRUE                                   1
#define AL_SOURCE_RELATIVE                        0x202
#define AL_PITCH                                  0x1003
#define AL_POSITION                               0x1004
#define AL_VELOCITY                               0x1006
#define AL_LOOPING                                0x1007
#define AL_BUFFER                                 0x1009
#define AL_GAIN                                   0x100A
#define AL_ORIENTATION                            0x100F
#define AL_REFERENCE_DISTANCE                     0x1020
#define AL_ROLLOFF_FACTOR                         0x1021
#define AL_MAX_DISTANCE                           0x1023
#define AL_FORMAT_MONO8                           0x1100
#define AL_FORMAT_MONO16                          0x1101
#define AL_FORMAT_STEREO8                         0x1102
#define AL_FORMAT_STEREO16                        0x1103
#define AL_INVALID_NAME                           0xA001
#define AL_INVALID_ENUM                           0xA002
#define AL_INVALID_VALUE                          0xA003
#define AL_INVALID_OPERATION                      0xA004
#define AL_OUT_OF_MEMORY                          0xA005
#define AL_VENDOR                                 0xB001
#define AL_VERSION                                0xB002
#define AL_RENDERER                               0xB003
#define AL_EXTENSIONS                             0xB004
#define AL_DISTANCE_MODEL                         0xD000
#define AL_INVERSE_DISTANCE                       0xD001
#define AL_INVERSE_DISTANCE_CLAMPED               0xD002
#define AL_LINEAR_DISTANCE                        0xD003
#define AL_LINEAR_DISTANCE_CLAMPED                0xD004
#define AL_EXPONENT_DISTANCE                      0xD005
#define AL_EXPONENT_DISTANCE_CLAMPED              0xD006




#if defined(_WIN32)
 #define ALC_APIENTRY __cdecl
#else
 #define ALC_APIENTRY
#endif
#define ALC_API

typedef char ALCboolean;
typedef char ALCchar;
typedef int ALCint;
typedef int ALCenum;
typedef struct ALCdevice_struct ALCdevice;
typedef struct ALCcontext_struct ALCcontext;

static ALC_API ALCdevice *     (ALC_APIENTRY *palcOpenDevice)( const ALCchar *devicename );
static ALC_API ALCboolean      (ALC_APIENTRY *palcCloseDevice)( ALCdevice *device );

static ALC_API ALCcontext *    (ALC_APIENTRY *palcCreateContext)( ALCdevice *device, const ALCint* attrlist );
static ALC_API void            (ALC_APIENTRY *palcDestroyContext)( ALCcontext *context );
static ALC_API ALCboolean      (ALC_APIENTRY *palcMakeContextCurrent)( ALCcontext *context );
static ALC_API void            (ALC_APIENTRY *palcProcessContext)( ALCcontext *context );

static ALC_API const ALCchar * (ALC_APIENTRY *palcGetString)( ALCdevice *device, ALCenum param );
static ALC_API ALCboolean      (ALC_APIENTRY *palcIsExtensionPresent)( ALCdevice *device, const ALCchar *extname );

#define ALC_DEFAULT_DEVICE_SPECIFIER             0x1004
#define ALC_DEVICE_SPECIFIER                     0x1005
#define ALC_EXTENSIONS                           0x1006

//#include "AL/alut.h"
//#include "AL/al.h"
//#include "AL/alext.h"




#define SOUNDVARS "OpenAL variables"


extern sfx_t *known_sfx;
extern int loaded_sfx;
extern int num_sfx;

static qboolean openal_vars_initialized = false;


static void OnChangeALMaxDistance (cvar_t *var, char *value);
static void OnChangeALSpeedOfSound (cvar_t *var, char *value);
static void OnChangeALDopplerFactor (cvar_t *var, char *value);
static void OnChangeALDistanceModel (cvar_t *var, char *value);
/*
static void S_Init_f(void);
static void S_Info(void);

static void S_Shutdown_f(void);
*/
static cvar_t s_al_enable = CVAR("s_al_enable", "0");
static cvar_t s_al_debug = CVAR("s_al_debug", "0");
static cvar_t s_al_max_distance = CVARFC("s_al_max_distance", "1000",0,OnChangeALMaxDistance);
static cvar_t s_al_speedofsound = CVARFC("s_al_speedofsound", "343.3",0,OnChangeALSpeedOfSound);
static cvar_t s_al_dopplerfactor = CVARFC("s_al_dopplerfactor", "3.0",0,OnChangeALDopplerFactor);
static cvar_t s_al_distancemodel = CVARFC("s_al_distancemodel", "1",0,OnChangeALDistanceModel);
static cvar_t s_al_rolloff_factor = CVAR("s_al_rolloff_factor", "1");
static cvar_t s_al_reference_distance = CVAR("s_al_reference_distance", "120");static cvar_t s_al_velocityscale = CVAR("s_al_velocityscale", "1");
static cvar_t s_al_static_listener = CVAR("s_al_static_listener", "0");	//cheat

#define NUM_SOURCES MAX_CHANNELS
static ALuint source[NUM_SOURCES];

static ALCdevice *OpenAL_Device;
static ALCcontext *OpenAL_Context;

static ALfloat ListenPos[] = { 0.0, 0.0, 0.0 };

// Velocity of the listener.
static ALfloat ListenVel[] = { 0.0, 0.0, 0.0 };

// Orientation of the listener. (first 3 elements are "at", second 3 are "up")
static ALfloat ListenOri[] = { 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 };

static void PrintALError(char *string)
{
	ALenum err;
	char *text = NULL;
	if (!s_al_debug.value)
		return;
	err = palGetError();
	switch(err)
	{
	case 0:
		return;
	case AL_INVALID_NAME:
		text = "invalid name";
		break;
	case AL_INVALID_ENUM:
		text = "invalid enum";
		break;
	case AL_INVALID_VALUE:
		text = "invalid value";
		break;
	case AL_INVALID_OPERATION:
		text = "invalid operation";
		break;
	case AL_OUT_OF_MEMORY:
		text = "out of memory";
		break;
	default:
		text = "unknown";
		break;
	}
	Con_Printf("OpenAL - %s: %x: %s\n",string,err,text);
}

void OpenAL_LoadCache(sfx_t *s, sfxcache_t *sc)
{
	unsigned int fmt;
	unsigned int size;
	switch(sc->width)
	{
	case 1:
		if (sc->numchannels == 2)
		{
			fmt = AL_FORMAT_STEREO8;
			size = sc->length*2;
		}
		else
		{
			fmt = AL_FORMAT_MONO8;
			size = sc->length*1;
		}
		break;
	case 2:
		if (sc->numchannels == 2)
		{
			fmt = AL_FORMAT_STEREO16;
			size = sc->length*4;
		}
		else
		{
			fmt = AL_FORMAT_MONO16;
			size = sc->length*2;
		}
		break;
	default:
		return;
	}
	PrintALError("pre Buffer Data");
	palGenBuffers(1, &s->openal_buffer);
	/*openal is inconsistant and supports only 8bit unsigned or 16bit signed*/
	if (sc->width == 1)
	{
		unsigned char *tmp = malloc(size);
		char *src = sc->data;
		int i;
		for (i = 0; i < size; i++)
		{
			tmp[i] = src[i]+128;
		}
		palBufferData(s->openal_buffer, fmt, tmp, size, sc->speed);
		free(tmp);
	}
	else
		palBufferData(s->openal_buffer, fmt, sc->data, size, sc->speed);

	//FIXME: we need to handle oal-oom error codes

	PrintALError("Buffer Data");
}

void OpenAL_CvarInit(void)
{
	Cvar_Register(&s_al_enable, SOUNDVARS);
	Cvar_Register(&s_al_debug, SOUNDVARS);
	Cvar_Register(&s_al_max_distance, SOUNDVARS);
	Cvar_Register(&s_al_dopplerfactor, SOUNDVARS);
	Cvar_Register(&s_al_distancemodel, SOUNDVARS);
	Cvar_Register(&s_al_reference_distance, SOUNDVARS);
	Cvar_Register(&s_al_rolloff_factor, SOUNDVARS);
	Cvar_Register(&s_al_velocityscale, SOUNDVARS);
	Cvar_Register(&s_al_static_listener, SOUNDVARS);
	Cvar_Register(&s_al_speedofsound, SOUNDVARS);
}

extern float voicevolumemod;
void OpenAL_Update_Listener(vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t velocity)
{
	VectorScale(velocity, s_al_velocityscale.value, ListenVel);
	VectorCopy(origin, ListenPos);

	ListenOri[0] = forward[0];
	ListenOri[1] = forward[1];
	ListenOri[2] = forward[2];
	ListenOri[3] = up[0];
	ListenOri[4] = up[1];
	ListenOri[5] = up[2];


	if (!s_al_static_listener.value)
	{
		palListenerf(AL_GAIN, volume.value*voicevolumemod);
		palListenerfv(AL_POSITION, ListenPos);
		palListenerfv(AL_VELOCITY, ListenVel);
		palListenerfv(AL_ORIENTATION, ListenOri);
	}
}

/*
static void OpenAL_StopAllSounds(qboolean clear)
{
	Con_Printf("-------------------------- %i ---------------\n",clear);
	palSourceStopv(NUM_SOURCES,source);
	palSourceStopv(NUM_SOURCES,static_source);
	num_sfx=0;
	num_static_source=0;
	if (clear)
	{
	}
}
*/
/*
void OpenAL_StartSound(int entnum, int entchannel, sfx_t * sfx, vec3_t origin, float fvol, float attenuation, float pitch)
{
	vec3_t	tmp;
	extern cvar_t temp1;
	if (!temp1.value)
		temp1.value = 1;

	if (!sfx->openal_buffer)
	{
		sfxcache_t *sc = Cache_Check(&sfx->cache);
		if (!sc)
			return;
		OpenAL_LoadCache(sfx, sc);
	}

	if (!origin)
		VectorClear(tmp);
	else
	{
		tmp[0] = origin[0];
		tmp[1] = origin[1];
		tmp[2] = origin[2];
	}

	PrintALError("pre start sound");

	palSourceStop(source[num_sfx]);
	palSourcei(source[num_sfx], AL_BUFFER, sfx->openal_buffer);
	palSourcef(source[num_sfx], AL_PITCH, pitch*temp1.value);
	palSourcef(source[num_sfx], AL_GAIN, fvol);
//	palSourcef(source[num_sfx], AL_MAX_DISTANCE, s_al_max_distance.value);
//	palSourcef(source[num_sfx], AL_ROLLOFF_FACTOR, s_al_rolloff_factor.value);
	palSourcefv(source[num_sfx], AL_POSITION, tmp);
	palSourcefv(source[num_sfx], AL_VELOCITY, vec3_origin);
	palSourcef(source[num_sfx], AL_REFERENCE_DISTANCE, s_al_reference_distance.value);

	if (entnum == -1 || entnum == cl.playernum[0]+1)
	{
		palSourcei(source[num_sfx], AL_SOURCE_RELATIVE, AL_TRUE);
		palSourcef(source[num_sfx], AL_ROLLOFF_FACTOR, 0.0f);
	}
	else
	{
		palSourcei(source[num_sfx], AL_SOURCE_RELATIVE, AL_FALSE);
		palSourcef(source[num_sfx], AL_ROLLOFF_FACTOR, s_al_rolloff_factor.value);
	}

	palSourcePlay(source[num_sfx]);
	num_sfx++;
	if (num_sfx >= NUM_SOURCES)
		num_sfx =0;

	PrintALError("post start sound");
}*/

static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned int schanged)
{
	ALuint src;
	sfx_t *sfx = chan->sfx;
	float pitch;

	src = source[chan - sc->channel];
	if (!src)
	{
		if (!sfx || chan->master_vol == 0)
			return;
		palGenSources(1, &src);
		source[chan - sc->channel] = src;
		schanged = true;
	}

	PrintALError("pre start sound");

	if (schanged && src)
		palSourceStop(src);

	/*just wanted to stop it?*/
	if (!sfx || chan->master_vol == 0)
	{
		if (src)
		{
			palDeleteBuffers(1, &src);
			source[chan - sc->channel] = 0;
		}
		return;
	}

	if (schanged)
	{
		if (!sfx->openal_buffer)
		{
			sfxcache_t *sc = S_LoadSound(sfx);
			if (!sc)	/*ack! can't start it if its not loaded!*/
				return;
			OpenAL_LoadCache(sfx, sc);
		}

		palSourcei(src, AL_BUFFER, sfx->openal_buffer);
	}
	palSourcef(src, AL_GAIN, chan->master_vol/255.0f);
//	palSourcef(src, AL_MAX_DISTANCE, s_al_max_distance.value);
//	palSourcef(src, AL_ROLLOFF_FACTOR, s_al_rolloff_factor.value);
	palSourcefv(src, AL_POSITION, chan->origin);
	palSourcefv(src, AL_VELOCITY, vec3_origin);

	if (schanged)
	{
		pitch = (float)chan->rate/(1<<PITCHSHIFT);
		palSourcef(src, AL_PITCH, pitch);

		palSourcef(src, AL_REFERENCE_DISTANCE, s_al_reference_distance.value);
		palSourcei(src, AL_LOOPING, chan->looping?AL_TRUE:AL_FALSE);
		if (chan->entnum == -1 || chan->entnum == cl.playernum[0]+1)
		{
			palSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
			palSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
		}
		else
		{
			palSourcei(src, AL_SOURCE_RELATIVE, AL_FALSE);
			palSourcef(src, AL_ROLLOFF_FACTOR, s_al_rolloff_factor.value*chan->dist_mult);
		}

	/*and start it up again*/
		palSourcePlay(src);
	}

	PrintALError("post start sound");
}

/*
static void S_Info (void)
{
	if (OpenAL_Device == NULL)
		return;

	Con_Printf("OpenAL Version        : %s\n",palGetString(AL_VERSION));
	Con_Printf("OpenAL Vendor         : %s\n",palGetString(AL_VENDOR));
	Con_Printf("OpenAL Renderer       : %s\n",palGetString(AL_RENDERER));
	if(palcIsExtensionPresent(NULL, (const ALCchar*)"ALC_ENUMERATION_EXT")==AL_TRUE)
	{
		Con_Printf("OpenAL Device         : %s\n",palcGetString(OpenAL_Device,ALC_DEVICE_SPECIFIER));
	}
	Con_Printf("OpenAL Default Device : %s\n",palcGetString(OpenAL_Device,ALC_DEFAULT_DEVICE_SPECIFIER));
	Con_Printf("OpenAL AL Extension   : %s\n",palGetString(AL_EXTENSIONS));
	Con_Printf("OpenAL ALC Extension  : %s\n",palcGetString(NULL,ALC_EXTENSIONS));
}
*/

static qboolean OpenAL_Init(void)
{
	dllfunction_t openalfuncs[] =
	{
		{(void*)&palGetError, "alGetError"},
		{(void*)&palSourcef, "alSourcef"},
		{(void*)&palSourcei, "alSourcei"},
		{(void*)&palSourcePlayv, "alSourcePlayv"},
		{(void*)&palSourceStopv, "alSourceStopv"},
		{(void*)&palSourcePlay, "alSourcePlay"},
		{(void*)&palSourceStop, "alSourceStop"},
		{(void*)&palDopplerFactor, "alDopplerFactor"},
		{(void*)&palGenBuffers, "alGenBuffers"},
		{(void*)&palIsBuffer, "alIsBuffer"},
		{(void*)&palBufferData, "alBufferData"},
		{(void*)&palDeleteBuffers, "alDeleteBuffers"},
		{(void*)&palListenerfv, "alListenerfv"},
		{(void*)&palSourcefv, "alSourcefv"},
		{(void*)&palGetString, "alGetString"},
		{(void*)&palGenSources, "alGenSources"},
		{(void*)&palListenerf, "alListenerf"},
		{(void*)&palDeleteSources, "alDeleteSources"},
		{(void*)&palSpeedOfSound, "alSpeedOfSound"},
		{(void*)&palDistanceModel, "alDistanceModel"},

		{(void*)&palcOpenDevice, "alcOpenDevice"},
		{(void*)&palcCloseDevice, "alcCloseDevice"},
		{(void*)&palcCreateContext, "alcCreateContext"},
		{(void*)&palcDestroyContext, "alcDestroyContext"},
		{(void*)&palcMakeContextCurrent, "alcMakeContextCurrent"},
		{(void*)&palcProcessContext, "alcProcessContext"},
		{(void*)&palcGetString, "alcGetString"},
		{(void*)&palcIsExtensionPresent, "alcIsExtensionPresent"},
		{NULL}
	};
	if (!Sys_LoadLibrary("OpenAL32", openalfuncs))
	{
		Con_Printf("OpenAL is not installed\n");
		return false;
	}

	OpenAL_Device = palcOpenDevice(NULL);
	if (OpenAL_Device == NULL)
	{
		PrintALError("Could not init a sound device\n");
		return false;
	}

	OpenAL_Context = palcCreateContext(OpenAL_Device, NULL);
	palcMakeContextCurrent(OpenAL_Context);
//	palcProcessContext(OpenAL_Context);

	//S_Info();


	palGenSources(NUM_SOURCES, source);
	PrintALError("alGensources for normal sources");


	palListenerfv(AL_POSITION, ListenPos);
	palListenerfv(AL_VELOCITY, ListenVel);
	palListenerfv(AL_ORIENTATION, ListenOri);

	return true;
}

static void OnChangeALMaxDistance (cvar_t *var, char *oldvalue)
{
}
static void OnChangeALSpeedOfSound (cvar_t *var, char *value)
{
	if (palSpeedOfSound)
		palSpeedOfSound(var->value);
}
static void OnChangeALDopplerFactor (cvar_t *var, char *oldvalue)
{
	if (palDopplerFactor)
		palDopplerFactor(var->value);
}
static void OnChangeALDistanceModel (cvar_t *var, char *value)
{
	if (!palDistanceModel)
		return;

	switch (var->ival)
	{
		case 0:
			palDistanceModel(AL_INVERSE_DISTANCE);
			break;
		case 1:
			palDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
			break;
		case 2:
			palDistanceModel(AL_LINEAR_DISTANCE);
			break;
		case 3:
			palDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
			break;
		case 4:
			palDistanceModel(AL_EXPONENT_DISTANCE);
			break;
		case 5:
			palDistanceModel(AL_EXPONENT_DISTANCE_CLAMPED);
			break;
		case 6:
			palDistanceModel(AL_NONE);
			break;
		default:
			Cvar_ForceSet(var, "0");
	}	
}

/*stub should not be called*/
static void *OpenAL_LockBuffer (soundcardinfo_t *sc, unsigned int *sampidx)
{
	//Con_Printf("OpenAL: LockBuffer\n");
	return NULL;
}

/*stub should not be called*/
static void OpenAL_UnlockBuffer (soundcardinfo_t *sc, void *buffer)
{
	//Con_Printf("OpenAL: UnlockBuffer\n");
}

static void OpenAL_SetUnderWater (soundcardinfo_t *sc, qboolean underwater)
{
	//Con_Printf("OpenAL: SetUnderWater %i\n", underwater);
}

/*stub should not be called*/
static void OpenAL_Submit (soundcardinfo_t *sc, int start, int end)
{
	//Con_Printf("OpenAL: Submit\n");
}

/*stub should not be called*/
static unsigned int OpenAL_GetDMAPos (soundcardinfo_t *sc)
{
	//Con_Printf("OpenAL: GetDMAPos\n");
	return 0;
}

static void OpenAL_Shutdown (soundcardinfo_t *sc)
{
	int i;

	palDeleteSources(NUM_SOURCES, source);

	/*make sure the buffers are cleared from the sound effects*/
	for (i=0;i<num_sfx;i++)
	{
		if (known_sfx[i].openal_buffer)
		{
			palDeleteBuffers(1,&known_sfx[i].openal_buffer);
			known_sfx[i].openal_buffer = 0;
		}
	}

	palcDestroyContext(OpenAL_Context);
	OpenAL_Context = NULL;
	palcCloseDevice(OpenAL_Device);
	OpenAL_Device = NULL;
}


static int OpenAL_InitCard(soundcardinfo_t *sc, int cardnum)
{
	if (cardnum != 0)
		return 2;

	if (!s_al_enable.ival)
		return 2;

	Con_Printf("Initiating OpenAL sound device.\n");

	if (OpenAL_Init() == false)
	{
		Con_Printf(CON_ERROR "OpenAL init failed\n");
		return false;
	}

	sc->Lock = OpenAL_LockBuffer;
	sc->Unlock = OpenAL_UnlockBuffer;
	sc->SetWaterDistortion = OpenAL_SetUnderWater;
	sc->Submit = OpenAL_Submit;
	sc->Shutdown = OpenAL_Shutdown;
	sc->GetDMAPos = OpenAL_GetDMAPos;
	sc->ChannelUpdate = OpenAL_ChannelUpdate;

	Q_snprintfz(sc->name, sizeof(sc->name), "OpenAL device");

	sc->openal = 1;
	sc->inactive_sound = true;
	sc->selfpainting = true;

	Cvar_ForceCallback(&s_al_distancemodel);
	Cvar_ForceCallback(&s_al_speedofsound);
	Cvar_ForceCallback(&s_al_dopplerfactor);
	Cvar_ForceCallback(&s_al_max_distance);
	return true;
}

int (*pOPENAL_InitCard) (soundcardinfo_t *sc, int cardnum) = &OpenAL_InitCard;

#endif