2004-09-19 17:57:20 +02:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
#include "res/res.h"
|
|
|
|
#include "res/snd.h"
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
#include <sstream>
|
|
|
|
|
2004-09-19 17:57:20 +02:00
|
|
|
#ifdef __APPLE__
|
|
|
|
# include <OpenAL/alut.h>
|
|
|
|
#else
|
|
|
|
# include <AL/al.h>
|
2004-10-05 15:11:28 +02:00
|
|
|
# include <AL/alc.h>
|
2004-09-19 17:57:20 +02:00
|
|
|
# include <AL/alut.h>
|
|
|
|
#endif
|
|
|
|
|
2004-09-22 17:20:58 +02:00
|
|
|
// Linux OpenAL puts the Ogg Vorbis extension enums in alexttypes.h
|
|
|
|
#ifdef OS_LINUX
|
|
|
|
# include <AL/alexttypes.h>
|
|
|
|
#endif
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// for DLL-load hack in alc_init
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include "sysdep/win/win_internal.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-09-19 17:57:20 +02:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma comment(lib, "openal32.lib")
|
|
|
|
#pragma comment(lib, "alut.lib")
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
#include "ogghack.h"
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
#include "timer.h"
|
2004-10-05 15:11:28 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
static bool al_initialized = false;
|
2004-10-05 15:11:28 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
static const char* alc_dev_name = "MMSYSTEM";
|
2004-10-05 15:11:28 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
//static const char* alc_dev_name = 0;
|
2004-10-05 15:11:28 +02:00
|
|
|
// default: use OpenAL default device.
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
|
2004-09-19 17:57:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
// rationale for "buffer" handle and separate handle for each sound instance:
|
|
|
|
// - need access to sound instances to fade them out / loop for an unknown period
|
|
|
|
// - access via handle for safety
|
|
|
|
// - don't want to reload sound data every time => need one central instance
|
|
|
|
// that owns the data
|
2004-10-05 15:11:28 +02:00
|
|
|
// - want to support normal reload mechanism (for consistency if not necessity)
|
2004-09-19 17:57:20 +02:00
|
|
|
// - could hack something via h_find / if so, create new handle with fn_key = 0,
|
|
|
|
// but that would break reloading and is dodgy. we will create a new handle
|
|
|
|
// type instead.
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
static void al_check(const char* caller = "unknown")
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
assert(al_initialized);
|
|
|
|
|
|
|
|
ALenum err = alGetError();
|
|
|
|
if(err == AL_NO_ERROR)
|
2004-09-19 17:57:20 +02:00
|
|
|
return;
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
const char* str = (const char*)alGetString(err);
|
2004-10-16 22:54:24 +02:00
|
|
|
debug_out("openal error: %s; called from %s", str, caller);
|
2004-10-05 15:11:28 +02:00
|
|
|
debug_warn("OpenAL error");
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// listener: owns position/orientation and master gain.
|
|
|
|
// if they're set before al_initialized, we pass the saved values to
|
|
|
|
// OpenAL immediately after init (instead of waiting until next update).
|
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static float al_listener_gain = 1.0;
|
|
|
|
static float al_listener_pos[3];
|
|
|
|
static float al_listener_orientation[6];
|
|
|
|
// float view_direction[3], up_vector[3]; passed directly to OpenAL
|
|
|
|
|
|
|
|
|
|
|
|
// also called from al_init.
|
|
|
|
static void al_listener_latch()
|
|
|
|
{
|
|
|
|
if(al_initialized)
|
|
|
|
{
|
|
|
|
alListenerf(AL_GAIN, al_listener_gain);
|
|
|
|
alListenerfv(AL_POSITION, al_listener_pos);
|
|
|
|
alListenerfv(AL_ORIENTATION, al_listener_orientation);
|
|
|
|
al_check("al_listener_latch");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int snd_set_master_gain(float gain)
|
|
|
|
{
|
|
|
|
if(gain < 0)
|
|
|
|
{
|
|
|
|
debug_warn("snd_set_master_gain: gain < 0");
|
|
|
|
return ERR_INVALID_PARAM;
|
|
|
|
}
|
|
|
|
|
|
|
|
al_listener_gain = gain;
|
|
|
|
|
|
|
|
al_listener_latch();
|
|
|
|
// position will get sent too. this isn't called often, so we don't care.
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void al_listener_set_pos(const float pos[3], const float dir[3], const float up[3])
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < 3; i++)
|
|
|
|
al_listener_pos[i] = pos[i];
|
|
|
|
for(i = 0; i < 3; i++)
|
|
|
|
al_listener_orientation[i] = dir[i];
|
|
|
|
for(i = 0; i < 3; i++)
|
|
|
|
al_listener_orientation[3+i] = up[i];
|
|
|
|
|
|
|
|
al_listener_latch();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static float al_listener_dist_2(const float point[3])
|
|
|
|
{
|
|
|
|
const float dx = al_listener_pos[0] - point[0];
|
|
|
|
const float dy = al_listener_pos[1] - point[1];
|
|
|
|
const float dz = al_listener_pos[2] - point[2];
|
|
|
|
return dx*dx + dy*dy + dz*dz;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2004-10-15 15:18:34 +02:00
|
|
|
//
|
2004-10-16 22:54:24 +02:00
|
|
|
// AL buffer suballocator: allocates buffers as needed (alGenBuffers is fast).
|
|
|
|
// this interface is a bit more convenient than the OpenAL routines, and we
|
|
|
|
// verify that all buffers have been freed at exit.
|
2004-10-15 15:18:34 +02:00
|
|
|
//
|
2004-10-16 22:54:24 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2004-10-15 15:18:34 +02:00
|
|
|
|
|
|
|
static int al_bufs_outstanding;
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
static ALuint al_buf_alloc(ALvoid* data, ALsizei size, ALenum al_fmt, ALsizei al_freq)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
ALuint al_buf;
|
|
|
|
alGenBuffers(1, &al_buf);
|
|
|
|
alBufferData(al_buf, al_fmt, data, size, al_freq);
|
2004-10-16 22:54:24 +02:00
|
|
|
al_check("al_buf_alloc");
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
al_bufs_outstanding++;
|
2004-10-05 15:11:28 +02:00
|
|
|
return al_buf;
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
static void al_buf_free(ALuint al_buf)
|
|
|
|
{
|
|
|
|
assert(alIsBuffer(al_buf));
|
2004-10-16 22:54:24 +02:00
|
|
|
alDeleteBuffers(1, &al_buf);
|
|
|
|
al_check("al_buf_free");
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
al_bufs_outstanding--;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
// called from al_shutdown.
|
2004-10-15 15:18:34 +02:00
|
|
|
static void al_buf_shutdown()
|
|
|
|
{
|
|
|
|
assert(al_bufs_outstanding == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-19 17:57:20 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
2004-10-05 15:11:28 +02:00
|
|
|
// AL source suballocator: allocate all available sources up-front and
|
|
|
|
// pass them out as needed (alGenSources is quite slow, taking 3..5 ms per
|
|
|
|
// source returned). also responsible for enforcing user-specified limit
|
|
|
|
// on total number of sources (to reduce mixing cost on low-end systems).
|
2004-09-19 17:57:20 +02:00
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
static const int AL_SRC_MAX = 64;
|
|
|
|
// regardless of sound card caps, we won't use more than this
|
|
|
|
// (64 is just overkill).
|
|
|
|
static ALuint al_srcs[AL_SRC_MAX];
|
2004-10-16 22:54:24 +02:00
|
|
|
// FIFO stack of sources (first allocated is #0)
|
|
|
|
static int al_src_allocated;
|
|
|
|
// number of valid sources in al_srcs[] (set by al_src_init)
|
2004-10-05 15:11:28 +02:00
|
|
|
static int al_src_used = 0;
|
2004-10-16 22:54:24 +02:00
|
|
|
// number of sources currently in use
|
2004-10-05 15:11:28 +02:00
|
|
|
static int al_src_cap = AL_SRC_MAX;
|
|
|
|
// user-set limit on how many sources may be used
|
2004-09-19 17:57:20 +02:00
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// called from al_init.
|
|
|
|
static void al_src_init()
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
// grab as many sources as possible and count how many we get.
|
|
|
|
for(int i = 0; i < AL_SRC_MAX; i++)
|
|
|
|
{
|
|
|
|
alGenSources(1, &al_srcs[i]);
|
|
|
|
// we've reached the limit, no more are available.
|
|
|
|
if(alGetError() != AL_NO_ERROR)
|
|
|
|
break;
|
|
|
|
assert(alIsSource(al_srcs[i]));
|
|
|
|
al_src_allocated++;
|
|
|
|
}
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// limit user's cap to what we actually got.
|
|
|
|
if(al_src_cap > al_src_allocated)
|
|
|
|
al_src_cap = al_src_allocated;
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// make sure we got the minimum guaranteed by OpenAL.
|
|
|
|
assert(al_src_allocated >= 16);
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// release all sources on freelist (currently stack).
|
|
|
|
// all sources must have been returned to us via al_src_free.
|
|
|
|
// called from al_shutdown.
|
|
|
|
static void al_src_shutdown()
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
assert(al_src_used == 0);
|
|
|
|
alDeleteSources(al_src_allocated, al_srcs);
|
2004-10-16 22:54:24 +02:00
|
|
|
al_check("al_src_shutdown");
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
static ALuint al_src_alloc()
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
// no more to give
|
|
|
|
if(al_src_used >= al_src_cap)
|
|
|
|
return 0;
|
|
|
|
ALuint al_src = al_srcs[al_src_used++];
|
|
|
|
return al_src;
|
|
|
|
}
|
2004-09-19 17:57:20 +02:00
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
static void al_src_free(ALuint al_src)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
assert(alIsSource(al_src));
|
2004-10-05 15:11:28 +02:00
|
|
|
al_srcs[--al_src_used] = al_src;
|
2004-10-15 15:18:34 +02:00
|
|
|
assert(0 <= al_src_used && al_src_used < al_src_allocated);
|
|
|
|
// don't compare against cap - it might have been
|
|
|
|
// decreased to less than were in use.
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|
2004-09-19 17:57:20 +02:00
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
int snd_set_max_src(int cap)
|
|
|
|
{
|
|
|
|
// non-positive - bogus.
|
|
|
|
if(cap <= 0)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
debug_warn("snd_set_max_src: cap <= 0");
|
|
|
|
return -1;
|
|
|
|
}
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// either cap is legit (less than what we allocated in al_src_init),
|
|
|
|
// or al_src_init wasn't called yet. note: we accept anything in the
|
|
|
|
// second case, as al_src_init will sanity-check al_src_cap.
|
|
|
|
if(!al_src_allocated || cap < al_src_allocated)
|
|
|
|
{
|
|
|
|
al_src_cap = cap;
|
|
|
|
return 0;
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
2004-10-05 15:11:28 +02:00
|
|
|
// user is requesting a cap higher than what we actually allocated.
|
|
|
|
// that's fine (not an error), but we won't set the cap, since it
|
|
|
|
// determines how many sources may be returned.
|
|
|
|
else
|
|
|
|
return -1;
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
2004-10-05 15:11:28 +02:00
|
|
|
// OpenAL init / shutdown
|
2004-09-19 17:57:20 +02:00
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// called as late as possible, i.e. the first time sound/music is played
|
|
|
|
// (either from module init there, or from the play routine itself).
|
|
|
|
// this delays library load, leading to faster perceived app startup.
|
|
|
|
// no harm if called more than once.
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
static ALCcontext* alc_ctx;
|
|
|
|
static ALCdevice* alc_dev;
|
2004-09-19 17:57:20 +02:00
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
static void alc_shutdown()
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
alcMakeContextCurrent(0);
|
|
|
|
alcDestroyContext(alc_ctx);
|
|
|
|
alcCloseDevice(alc_dev);
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
static int alc_init()
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
// HACK: OpenAL loads and unloads these DLLs several times on Windows.
|
|
|
|
// we hold a reference to prevent the actual unload, thus speeding up
|
|
|
|
// sound startup by 100..400 ms. everything works ATM;
|
|
|
|
// hopefully, OpenAL doesn't rely on them actually being unloaded.
|
|
|
|
#ifdef _WIN32
|
|
|
|
HMODULE dlls[3];
|
|
|
|
dlls[0] = LoadLibrary("wrap_oal.dll");
|
|
|
|
dlls[1] = LoadLibrary("setupapi.dll");
|
|
|
|
dlls[2] = LoadLibrary("wdmaud.drv");
|
|
|
|
#endif
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
alc_dev = alcOpenDevice((ALubyte*)alc_dev_name);
|
|
|
|
if(alc_dev)
|
2004-09-19 21:29:03 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
alc_ctx = alcCreateContext(alc_dev, 0); // no attrlist needed
|
|
|
|
if(alc_ctx)
|
|
|
|
alcMakeContextCurrent(alc_ctx);
|
2004-09-19 21:29:03 +02:00
|
|
|
}
|
2004-10-05 15:11:28 +02:00
|
|
|
|
|
|
|
ALCenum err = alcGetError(alc_dev);
|
|
|
|
if(err != ALC_NO_ERROR || !alc_dev || !alc_ctx)
|
2004-09-19 21:29:03 +02:00
|
|
|
{
|
2004-10-16 22:54:24 +02:00
|
|
|
debug_out("alc_init failed. alc_dev=%p alc_ctx=%p err=%d\n", alc_dev, alc_ctx, err);
|
2004-09-19 21:29:03 +02:00
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// release DLL references, so BoundsChecker doesn't complain at exit
|
|
|
|
#ifdef _WIN32
|
|
|
|
for(int i = 0; i < ARRAY_SIZE(dlls); i++)
|
|
|
|
if(dlls[i] != INVALID_HANDLE_VALUE)
|
|
|
|
FreeLibrary(dlls[i]);
|
|
|
|
#endif
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
return ret;
|
|
|
|
}
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-09-21 20:43:58 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
|
2004-09-19 21:29:03 +02:00
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// called from each sound_open, and from snd_dev_set
|
|
|
|
static int al_init()
|
|
|
|
{
|
|
|
|
// only take action on first call, OR after snd_dev_set calls us again
|
|
|
|
if(al_initialized)
|
|
|
|
return 0;
|
|
|
|
al_initialized = true;
|
2004-09-19 21:29:03 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
CHECK_ERR(alc_init());
|
|
|
|
al_src_init(); // can't fail
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
al_listener_latch(); // can't fail
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
return 0;
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
static void al_shutdown()
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
al_src_shutdown();
|
|
|
|
al_buf_shutdown();
|
2004-10-05 15:11:28 +02:00
|
|
|
alc_shutdown();
|
2004-10-15 15:18:34 +02:00
|
|
|
|
|
|
|
al_initialized = false;
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// if OpenAL hasn't been initialized yet, we only remember the device
|
|
|
|
// name, which will be set when alc_init is later called; otherwise,
|
|
|
|
// OpenAL is reinitialized to use the desired device.
|
|
|
|
// (this is to speed up the common case of retrieving a device name from
|
|
|
|
// config files and setting it; OpenAL doesn't have to be loaded until
|
|
|
|
// sounds are actually played).
|
|
|
|
// return 0 to indicate success, or the status returned while initializing
|
|
|
|
// OpenAL.
|
|
|
|
static int al_reinit()
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
if(!al_initialized)
|
|
|
|
return 0;
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
al_shutdown();
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// was already using another device; now re-init
|
|
|
|
// (stops all currently playing sounds)
|
|
|
|
return al_init();
|
|
|
|
}
|
2004-09-19 17:57:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
2004-10-05 15:11:28 +02:00
|
|
|
//
|
|
|
|
// device enumeration: list all devices and allow the user to choose one,
|
|
|
|
// in case the default device has problems.
|
|
|
|
//
|
2004-09-19 17:57:20 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
static const char* devs;
|
|
|
|
// set by snd_dev_prepare_enum; used by snd_dev_next.
|
|
|
|
// consists of back-to-back C strings, terminated by an extra '\0'.
|
|
|
|
// (this is taken straight from OpenAL; dox say this format may change).
|
|
|
|
|
|
|
|
// prepare to enumerate all device names (this resets the list returned by
|
|
|
|
// snd_dev_next). return 0 on success, otherwise -1 (only if the requisite
|
2004-10-15 15:18:34 +02:00
|
|
|
// OpenAL extension isn't available). on failure, a "cannot enum device"
|
2004-10-05 15:11:28 +02:00
|
|
|
// message should be presented to the user, and snd_dev_set need not be
|
|
|
|
// called; OpenAL will use its default device.
|
|
|
|
// may be called each time the device list is needed.
|
|
|
|
int snd_dev_prepare_enum()
|
|
|
|
{
|
|
|
|
if(alcIsExtensionPresent(0, (ALubyte*)"ALC_ENUMERATION_EXT") != AL_TRUE)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
devs = (const char*)alcGetString(0, ALC_DEVICE_SPECIFIER);
|
|
|
|
return 0;
|
|
|
|
}
|
2004-09-19 17:57:20 +02:00
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// return the next device name, or 0 if all have been returned.
|
|
|
|
// do not call unless snd_dev_prepare_enum succeeded!
|
|
|
|
// not thread-safe! (static data from snd_dev_prepare_enum is used)
|
|
|
|
const char* snd_dev_next()
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
if(!*devs)
|
|
|
|
return 0;
|
|
|
|
const char* dev = devs;
|
|
|
|
devs += strlen(dev)+1;
|
|
|
|
return dev;
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
// tell OpenAL to use the specified device (0 to revert to default) in future.
|
2004-10-05 15:11:28 +02:00
|
|
|
//
|
|
|
|
// if OpenAL hasn't been initialized yet, we only remember the device
|
|
|
|
// name, which will be set when snd_init is later called; otherwise,
|
|
|
|
// OpenAL is reinitialized to use the desired device (thus stopping all
|
|
|
|
// active sounds). we go to this trouble to speed up perceived load times:
|
|
|
|
// OpenAL doesn't need to be loaded until sounds are actually played.
|
|
|
|
//
|
|
|
|
// return 0 on success, or the status returned while re-initializing OpenAL.
|
|
|
|
int snd_dev_set(const char* new_alc_dev_name)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
// requesting a specific device
|
|
|
|
if(new_alc_dev_name)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
// already using that device - done
|
|
|
|
if(alc_dev_name && !strcmp(alc_dev_name, new_alc_dev_name))
|
|
|
|
return 0;
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// store name (need to copy it, since we snd_init later,
|
|
|
|
// and it must then still be valid)
|
|
|
|
static char buf[32];
|
|
|
|
strncpy(buf, new_alc_dev_name, 32-1);
|
|
|
|
alc_dev_name = buf;
|
|
|
|
}
|
|
|
|
// requesting default device
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// already using default device - done
|
|
|
|
if(alc_dev_name == 0)
|
|
|
|
return 0;
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
alc_dev_name = 0;
|
|
|
|
}
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
return al_reinit();
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
// rationale: could make a case for a separate IO layer, but there's a
|
|
|
|
// problem: IOs need to be discarded after their data has been processed.
|
|
|
|
// if the IO layer is separate, we'd either need a callback from
|
|
|
|
// io_complete, passing the buffer to OpenAL, or mark IO slots as
|
|
|
|
// "discardable", so that they are freed the next io_issue.
|
|
|
|
// both are ugly; we instead integrate IO into the sound data code.
|
|
|
|
// IOs are passed to OpenAL and the discarded immediately.
|
|
|
|
//
|
|
|
|
// having one IO-queue per sound data object is no problem:
|
|
|
|
// we suballocate buffers, and don't need centralized scheduling
|
|
|
|
// (there will be <= 2 streams active at once).
|
|
|
|
|
|
|
|
|
2004-09-19 17:57:20 +02:00
|
|
|
// one stream apiece for music and voiceover (narration during tutorial).
|
|
|
|
// allowing more is possible, but would be inefficent due to seek overhead.
|
|
|
|
// set this limit to catch questionable usage (e.g. streaming normal sounds).
|
|
|
|
static const int MAX_STREAMS = 2;
|
|
|
|
|
|
|
|
static const int MAX_IOS = 4;
|
|
|
|
|
|
|
|
static const int TOTAL_IOS = MAX_STREAMS * MAX_IOS;
|
|
|
|
|
|
|
|
static const size_t RAW_BUF_SIZE = 32*KB;
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// I/O buffer suballocator for streamed sounds, to avoid
|
|
|
|
// frequent alloc/frees and therefore heap fragmentation.
|
2004-10-15 15:18:34 +02:00
|
|
|
//
|
2004-10-16 22:54:24 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
// note: snd_shutdown is called after h_mgr_shutdown,
|
2004-10-16 22:54:24 +02:00
|
|
|
// so all mem_alloc-ed blocks will already have been freed.
|
2004-10-15 15:18:34 +02:00
|
|
|
// we don't want our alloc to show up as a leak,
|
2004-10-16 22:54:24 +02:00
|
|
|
// so we use malloc and do the alignment ourselves.
|
|
|
|
|
|
|
|
static const size_t TOTAL_BUF_SIZE = TOTAL_IOS*RAW_BUF_SIZE;
|
|
|
|
// (not including padding for alignment)
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
static void* io_bufs_raw;
|
2004-10-16 22:54:24 +02:00
|
|
|
// raw, unaligned memory for all buffers (from malloc)
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
static void* io_buf_freelist;
|
2004-10-16 22:54:24 +02:00
|
|
|
// list of free buffers. start of buffer holds pointer to next in list.
|
|
|
|
|
|
|
|
|
|
|
|
static void io_buf_free(void* p)
|
|
|
|
{
|
|
|
|
assert(io_bufs_raw <= p && p <= (char*)io_bufs_raw+TOTAL_BUF_SIZE);
|
|
|
|
*(void**)p = io_buf_freelist;
|
|
|
|
io_buf_freelist = p;
|
|
|
|
}
|
|
|
|
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
// called from first io_buf_alloc.
|
2004-10-15 15:18:34 +02:00
|
|
|
static void io_buf_init()
|
|
|
|
{
|
2004-10-16 22:54:24 +02:00
|
|
|
// allocate 1 big aligned block for all buffers
|
2004-10-15 15:18:34 +02:00
|
|
|
const size_t align = 4*KB;
|
2004-10-16 22:54:24 +02:00
|
|
|
io_bufs_raw = malloc(TOTAL_BUF_SIZE + align-1);
|
|
|
|
// .. failed; io_buf_alloc calls will return 0
|
|
|
|
if(!io_bufs_raw)
|
|
|
|
return;
|
2004-10-15 15:18:34 +02:00
|
|
|
void* bufs = (void*)round_up((uintptr_t)io_bufs_raw, align);
|
2004-10-16 22:54:24 +02:00
|
|
|
|
|
|
|
// build freelist
|
|
|
|
char* p = (char*)bufs;
|
|
|
|
for(int i = 0; i < TOTAL_IOS; i++)
|
|
|
|
{
|
|
|
|
io_buf_free(p);
|
|
|
|
p += RAW_BUF_SIZE;
|
|
|
|
}
|
2004-10-15 15:18:34 +02:00
|
|
|
}
|
2004-09-19 17:57:20 +02:00
|
|
|
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
static void* io_buf_alloc()
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
ONCE(io_buf_init());
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
void* buf = io_buf_freelist;
|
2004-10-16 22:54:24 +02:00
|
|
|
// note: we have to bail now; can't update io_buf_freelist.
|
2004-10-15 15:18:34 +02:00
|
|
|
if(!buf)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-16 22:54:24 +02:00
|
|
|
if(!io_bufs_raw)
|
|
|
|
debug_warn("io_buf_alloc: not enough memory to allocate buffer pool");
|
|
|
|
else
|
|
|
|
debug_warn("io_buf_alloc: max #streams exceeded");
|
2004-10-15 15:18:34 +02:00
|
|
|
return 0;
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
io_buf_freelist = *(void**)io_buf_freelist;
|
|
|
|
return buf;
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
// no-op if io_buf_alloc was never called.
|
|
|
|
// called by snd_shutdown.
|
2004-10-15 15:18:34 +02:00
|
|
|
static void io_buf_shutdown()
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
free(io_bufs_raw);
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
2004-10-05 15:11:28 +02:00
|
|
|
// sound data provider
|
2004-09-19 17:57:20 +02:00
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
struct SndData
|
|
|
|
{
|
|
|
|
bool stream;
|
|
|
|
|
|
|
|
// clip
|
|
|
|
ALuint al_buf;
|
|
|
|
|
|
|
|
// stream
|
|
|
|
Handle hf;
|
|
|
|
ALenum al_fmt;
|
|
|
|
ALsizei al_freq;
|
2004-10-15 15:18:34 +02:00
|
|
|
Handle ios[MAX_IOS];
|
|
|
|
int active_ios;
|
2004-10-16 22:54:24 +02:00
|
|
|
|
|
|
|
void* o;
|
2004-10-05 15:11:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
H_TYPE_DEFINE(SndData);
|
2004-09-19 17:57:20 +02:00
|
|
|
|
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
// called from SndData_reload and snd_data_get_buf.
|
2004-10-15 15:18:34 +02:00
|
|
|
static int stream_issue(SndData* sd, Handle hf)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
if(sd->active_ios >= MAX_IOS)
|
|
|
|
return 0;
|
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
void* buf = io_buf_alloc();
|
|
|
|
if(!buf)
|
|
|
|
return ERR_NO_MEM;
|
|
|
|
Handle h = vfs_start_io(hf, RAW_BUF_SIZE, buf);
|
2004-10-15 15:18:34 +02:00
|
|
|
CHECK_ERR(h);
|
|
|
|
sd->ios[sd->active_ios++] = h;
|
|
|
|
return 0;
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
static void SndData_init(SndData* sd, va_list args)
|
2004-10-05 15:11:28 +02:00
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
sd->stream = va_arg(args, bool);
|
|
|
|
}
|
2004-10-05 15:11:28 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
static void SndData_dtor(SndData* sd)
|
|
|
|
{
|
|
|
|
debug_out("snd_data dtor\n");
|
2004-10-05 15:11:28 +02:00
|
|
|
if(sd->stream)
|
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
vfs_close(sd->hf);
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
al_buf_free(sd->al_buf);
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|
2004-10-15 15:18:34 +02:00
|
|
|
}
|
2004-10-05 15:11:28 +02:00
|
|
|
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
static int SndData_reload(SndData* sd, const char* fn, Handle)
|
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
//
|
|
|
|
// detect sound format by checking file extension
|
|
|
|
//
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
enum FileType
|
|
|
|
{
|
|
|
|
FT_WAV,
|
|
|
|
FT_OGG
|
|
|
|
}
|
|
|
|
file_type;
|
2004-10-05 15:11:28 +02:00
|
|
|
|
|
|
|
const char* ext = strrchr(fn, '.');
|
|
|
|
// .. OGG (data will be passed directly to OpenAL)
|
|
|
|
if(ext && !stricmp(ext, ".ogg"))
|
|
|
|
{
|
|
|
|
// first use of OGG: check if OpenAL extension is available.
|
|
|
|
// note: this is required! OpenAL does its init here.
|
2004-10-16 22:54:24 +02:00
|
|
|
/* static int ogg_supported = -1;
|
2004-10-05 15:11:28 +02:00
|
|
|
if(ogg_supported == -1)
|
|
|
|
ogg_supported = alIsExtensionPresent((ALubyte*)"AL_EXT_vorbis")? 1 : 0;
|
|
|
|
if(!ogg_supported)
|
2004-10-15 15:18:34 +02:00
|
|
|
return -1;
|
2004-10-16 22:54:24 +02:00
|
|
|
*/
|
2004-10-05 15:11:28 +02:00
|
|
|
sd->al_fmt = AL_FORMAT_VORBIS_EXT;
|
|
|
|
sd->al_freq = 0;
|
2004-10-15 15:18:34 +02:00
|
|
|
|
|
|
|
file_type = FT_OGG;
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|
|
|
|
// .. WAV
|
|
|
|
else if(ext && !stricmp(ext, ".wav"))
|
2004-10-15 15:18:34 +02:00
|
|
|
file_type = FT_WAV;
|
2004-10-05 15:11:28 +02:00
|
|
|
// .. unknown extension
|
|
|
|
else
|
2004-10-15 15:18:34 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
|
|
if(sd->stream)
|
2004-10-05 15:11:28 +02:00
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
// refuse to stream anything that cannot be passed directly to OpenAL -
|
|
|
|
// we'd have to extract the audio data ourselves (not worth it).
|
|
|
|
if(file_type != FT_OGG)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
sd->hf = vfs_open(fn);
|
|
|
|
CHECK_ERR(sd->hf);
|
|
|
|
|
|
|
|
for(int i = 0; i < MAX_IOS; i++)
|
|
|
|
CHECK_ERR(stream_issue(sd, sd->hf));
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|
2004-10-15 15:18:34 +02:00
|
|
|
// clip
|
|
|
|
else
|
|
|
|
{
|
|
|
|
void* file;
|
|
|
|
size_t file_size;
|
|
|
|
CHECK_ERR(vfs_load(fn, file, file_size));
|
2004-10-05 15:11:28 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
ALvoid* al_data = file;
|
|
|
|
ALsizei al_size = (ALsizei)file_size;
|
|
|
|
|
|
|
|
if(file_type == FT_WAV)
|
|
|
|
{
|
|
|
|
ALbyte* memory = (ALbyte*)file;
|
|
|
|
ALboolean al_loop; // unused
|
|
|
|
alutLoadWAVMemory(memory, &sd->al_fmt, &al_data, &al_size, &sd->al_freq, &al_loop);
|
|
|
|
}
|
2004-10-05 15:11:28 +02:00
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
|
|
|
|
std::vector<u8> data;
|
|
|
|
if(file_type == FT_OGG)
|
|
|
|
{
|
|
|
|
sd->o = ogg_create();
|
|
|
|
ogg_give_raw(sd->o, file, file_size);
|
|
|
|
ogg_open(sd->o, sd->al_fmt, sd->al_freq);
|
|
|
|
|
|
|
|
size_t datasize=0;
|
|
|
|
size_t bytes_read;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
const size_t bufsize = 32*KB;
|
|
|
|
char buf[bufsize];
|
|
|
|
bytes_read = ogg_read(sd->o, buf, bufsize);
|
|
|
|
data.resize(data.size() + bytes_read);
|
|
|
|
for(size_t i = 0; i < bytes_read; i++)
|
|
|
|
data[datasize+i] = buf[i];
|
|
|
|
datasize += bytes_read;
|
|
|
|
}
|
|
|
|
while(bytes_read > 0);
|
|
|
|
al_data = &data[0];
|
|
|
|
al_size = (ALsizei)datasize;
|
|
|
|
}
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
sd->al_buf = al_buf_alloc(al_data, al_size, sd->al_fmt, sd->al_freq);
|
2004-10-05 15:11:28 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
mem_free(file);
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
return 0;
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// open and return a handle to a sound file's data
|
|
|
|
static Handle snd_data_load(const char* const fn, const bool stream)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
// make sure we don't reload a stream object
|
|
|
|
uint flags = 0;
|
|
|
|
if(stream)
|
|
|
|
flags = RES_UNIQUE;
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
return h_alloc(H_SndData, fn, flags, stream);
|
|
|
|
}
|
2004-09-19 17:57:20 +02:00
|
|
|
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
// close the sound file data <hsd> and set hsd to 0.
|
2004-10-05 15:11:28 +02:00
|
|
|
static int snd_data_free(Handle& hsd)
|
|
|
|
{
|
|
|
|
return h_free(hsd, H_SndData);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum BufRet
|
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
// buffer has been returned; barring errors, more will be available.
|
2004-10-05 15:11:28 +02:00
|
|
|
BUF_OK = 0,
|
2004-10-15 15:18:34 +02:00
|
|
|
|
|
|
|
// this was the last buffer we will return (end of file reached).
|
2004-10-05 15:11:28 +02:00
|
|
|
BUF_EOF = 1,
|
2004-10-15 15:18:34 +02:00
|
|
|
|
|
|
|
// no buffer returned - still streaming in ATM. call again later.
|
|
|
|
BUF_AGAIN = 2,
|
|
|
|
|
|
|
|
// anything else: negative error code
|
2004-10-05 15:11:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static int snd_data_get_buf(Handle hsd, ALuint& al_buf)
|
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
// in case H_DEREF fails
|
|
|
|
al_buf = 0;
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
H_DEREF(hsd, SndData, sd);
|
|
|
|
|
|
|
|
// clip: just return buffer (which was created in snd_data_load)
|
|
|
|
if(sd->al_buf)
|
|
|
|
{
|
|
|
|
al_buf = sd->al_buf;
|
2004-10-15 15:18:34 +02:00
|
|
|
return BUF_EOF;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// stream: check if IO finished, issue next, return the completed buffer
|
|
|
|
//
|
|
|
|
|
|
|
|
assert(sd->active_ios);
|
|
|
|
|
|
|
|
// has it finished? if not, bail
|
|
|
|
Handle hio = sd->ios[0];
|
|
|
|
int is_complete = vfs_io_complete(hio);
|
|
|
|
CHECK_ERR(is_complete);
|
|
|
|
if(is_complete == 0)
|
|
|
|
return BUF_AGAIN;
|
|
|
|
|
|
|
|
// get its buffer
|
|
|
|
void* data;
|
|
|
|
size_t size;
|
|
|
|
CHECK_ERR(vfs_wait_io(hio, data, size));
|
|
|
|
// returns immediately, since vfs_io_complete == 1
|
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
al_buf = al_buf_alloc(data, (ALsizei)size, sd->al_fmt, sd->al_freq);
|
|
|
|
|
|
|
|
// free IO slot
|
|
|
|
int err = vfs_discard_io(hio);
|
|
|
|
if(err < 0)
|
|
|
|
{
|
|
|
|
al_buf_free(al_buf);
|
|
|
|
return err;
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
// we implement the required 'circular queue' as a stack;
|
|
|
|
// have to shift all items after this one down.
|
|
|
|
sd->active_ios--;
|
|
|
|
for(int i = 0; i < sd->active_ios; i++)
|
|
|
|
sd->ios[i] = sd->ios[i+1];
|
|
|
|
|
|
|
|
// try to issue the next IO. if EOF reached, indicate al_buf is the last.
|
|
|
|
err = stream_issue(sd, sd->hf);
|
|
|
|
if(err == ERR_EOF)
|
|
|
|
return BUF_EOF;
|
|
|
|
|
|
|
|
// al_buf valid and next IO issued successfully.
|
|
|
|
return BUF_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int snd_data_free_buf(Handle hsd, ALuint al_buf)
|
|
|
|
{
|
|
|
|
H_DEREF(hsd, SndData, sd);
|
|
|
|
|
|
|
|
// clip: no-op (caller will later release hsd reference;
|
|
|
|
// it won't actually be freed until exit, because it's cached).
|
|
|
|
if(!sd->stream)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
al_buf_free(al_buf);
|
|
|
|
return 0;
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// sound instance
|
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
struct VSrc
|
|
|
|
{
|
|
|
|
ALuint al_src;
|
|
|
|
|
|
|
|
// handle to this VSrc, so that it can close itself
|
|
|
|
Handle hvs;
|
|
|
|
|
|
|
|
// associated sound data
|
|
|
|
Handle hsd;
|
|
|
|
|
|
|
|
// - can't have 2 active instances of a streamed sound, so make sure
|
|
|
|
// caller is aware of the limitation by requiring them to set this.
|
|
|
|
bool stream;
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
bool eof;
|
|
|
|
bool closing;
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
ALfloat pos[3];
|
|
|
|
ALfloat gain;
|
|
|
|
ALboolean loop;
|
|
|
|
ALboolean relative;
|
|
|
|
|
|
|
|
float static_pri;
|
|
|
|
float cur_pri;
|
|
|
|
};
|
|
|
|
|
|
|
|
H_TYPE_DEFINE(VSrc);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
static int vsrc_update(VSrc* vs)
|
|
|
|
{
|
|
|
|
ALint _num_queued;
|
|
|
|
alGetSourcei(vs->al_src, AL_BUFFERS_QUEUED, &_num_queued);
|
|
|
|
|
|
|
|
|
|
|
|
// remove all finished buffers
|
|
|
|
int num_processed;
|
|
|
|
alGetSourcei(vs->al_src, AL_BUFFERS_PROCESSED, &num_processed);
|
|
|
|
debug_out("%g: vs=%p hvs=%I64x q=%d p=%d\n", get_time(), vs, vs->hvs, _num_queued, num_processed);
|
|
|
|
for(int i = 0; i < num_processed; i++)
|
|
|
|
{
|
|
|
|
ALuint al_buf;
|
|
|
|
alSourceUnqueueBuffers(vs->al_src, 1, &al_buf);
|
|
|
|
debug_out("removing processed buf=%p vs=%p src=%p hvs=%I64x\n", al_buf, vs, vs->al_src, vs->hvs);
|
|
|
|
CHECK_ERR(snd_data_free_buf(vs->hsd, al_buf));
|
|
|
|
}
|
|
|
|
|
|
|
|
if(vs->closing)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// no more buffers left, and EOF reached
|
|
|
|
ALint num_queued;
|
|
|
|
alGetSourcei(vs->al_src, AL_BUFFERS_QUEUED, &num_queued);
|
2004-10-16 22:54:24 +02:00
|
|
|
al_check("vsrc_update alGetSourcei");
|
2004-10-15 15:18:34 +02:00
|
|
|
|
|
|
|
if(num_queued == 0 && vs->eof)
|
|
|
|
{
|
|
|
|
Handle tmp = vs->hvs;
|
|
|
|
debug_out("%g: reached end, closing vs=%p src=%p hvs=%I64x\n", get_time(), vs, vs->al_src, vs->hvs);
|
|
|
|
snd_free(/*vs->hvs*/tmp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!vs->eof)
|
|
|
|
{
|
|
|
|
int to_fill = 4;
|
|
|
|
if(num_queued > 0)
|
|
|
|
to_fill = num_processed;
|
|
|
|
|
|
|
|
int ret;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
ALuint al_buf;
|
|
|
|
ret = snd_data_get_buf(vs->hsd, al_buf);
|
|
|
|
CHECK_ERR(ret);
|
|
|
|
|
|
|
|
debug_out("%g: got buf: buf=%p vs=%p src=%p hvs=%I64x\n", get_time(), al_buf, vs, vs->al_src, vs->hvs);
|
|
|
|
|
|
|
|
alSourceQueueBuffers(vs->al_src, 1, &al_buf);
|
2004-10-16 22:54:24 +02:00
|
|
|
al_check("vsrc_update SourceQueueBuffers");
|
2004-10-15 15:18:34 +02:00
|
|
|
}
|
|
|
|
while(to_fill-- && ret == BUF_OK);
|
|
|
|
|
|
|
|
if(ret == BUF_EOF)
|
|
|
|
{
|
|
|
|
vs->eof = true;
|
|
|
|
debug_out("EOF reported for vs=%p src=%p hvs=%I64x\n", vs, vs->al_src, vs->hvs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
static int vsrc_grant_src(VSrc* vs)
|
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
debug_out("grant vs=%p src=%p\n", vs, vs->al_src);
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// already playing - bail
|
|
|
|
if(vs->al_src)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
// try to alloc source
|
|
|
|
vs->al_src = al_src_alloc();
|
|
|
|
// called from 2 places: sound_play can't know if a source is available,
|
|
|
|
// so this isn't an error
|
|
|
|
if(!vs->al_src)
|
2004-10-15 15:18:34 +02:00
|
|
|
{
|
|
|
|
debug_out("grant couldn't alloc src!\n");
|
2004-10-05 15:11:28 +02:00
|
|
|
return -1;
|
2004-10-15 15:18:34 +02:00
|
|
|
}
|
2004-09-19 17:57:20 +02:00
|
|
|
|
|
|
|
// OpenAL docs don't specify default values, so initialize everything
|
|
|
|
// ourselves to be sure. note: alSourcefv param is not const.
|
|
|
|
float zero3[3] = { 0.0f, 0.0f, 0.0f };
|
2004-10-05 15:11:28 +02:00
|
|
|
alSourcefv(vs->al_src, AL_VELOCITY, zero3);
|
|
|
|
alSourcefv(vs->al_src, AL_DIRECTION, zero3);
|
|
|
|
alSourcef(vs->al_src, AL_ROLLOFF_FACTOR, 0.0f);
|
|
|
|
alSourcei(vs->al_src, AL_SOURCE_RELATIVE, AL_TRUE);
|
|
|
|
|
|
|
|
// we only now got a source, so latch previous settings
|
|
|
|
// don't use snd_set*; this way is easiest
|
|
|
|
alSourcef(vs->al_src, AL_GAIN, vs->gain);
|
|
|
|
alSourcefv(vs->al_src, AL_POSITION, vs->pos);
|
|
|
|
alSourcei(vs->al_src, AL_LOOPING, vs->loop);
|
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
al_check("vsrc_grant_src Source*");
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
CHECK_ERR(vsrc_update(vs));
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
debug_out("play vs=%p src=%p hvs=%I64x\n", vs, vs->al_src, vs->hvs);
|
2004-10-05 15:11:28 +02:00
|
|
|
alSourcePlay(vs->al_src);
|
2004-10-16 22:54:24 +02:00
|
|
|
al_check("vsrc_grant_src SourcePlay");
|
2004-10-05 15:11:28 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-09-19 21:29:03 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
static int vsrc_reclaim_src(VSrc* vs)
|
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
debug_out("reclaim vs=%p src=%p\n", vs, vs->al_src);
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// not playing - bail
|
|
|
|
if(!vs->al_src)
|
|
|
|
return 0;
|
2004-09-19 21:29:03 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
debug_out("stop\n");
|
2004-10-05 15:11:28 +02:00
|
|
|
alSourceStop(vs->al_src);
|
2004-10-16 22:54:24 +02:00
|
|
|
al_check("vsrc_reclaim_src SourceStop");
|
2004-09-19 21:29:03 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
vs->closing = true;
|
|
|
|
CHECK_ERR(vsrc_update(vs));
|
|
|
|
// (note: all buffers are now considered 'processed', since src is stopped)
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
al_src_free(vs->al_src);
|
|
|
|
return 0;
|
|
|
|
}
|
2004-09-19 21:29:03 +02:00
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void list_remove(VSrc*);
|
|
|
|
|
|
|
|
|
|
|
|
static void VSrc_init(VSrc* vs, va_list args)
|
|
|
|
{
|
|
|
|
vs->stream = va_arg(args, bool);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void VSrc_dtor(VSrc* vs)
|
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
debug_out("vsrc_dtor vs=%p\n hvs=%I64x\n", vs, vs->hvs);
|
2004-10-05 15:11:28 +02:00
|
|
|
list_remove(vs);
|
|
|
|
vsrc_reclaim_src(vs);
|
|
|
|
snd_data_free(vs->hsd);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int VSrc_reload(VSrc* vs, const char* def_fn, Handle hvs)
|
|
|
|
{
|
|
|
|
// cannot wait till play(), need to init here:
|
|
|
|
// must load OpenAL so that snd_data_load can check for OGG extension.
|
|
|
|
al_init();
|
|
|
|
|
|
|
|
/*
|
|
|
|
void* def_file;
|
|
|
|
size_t def_size;
|
|
|
|
CHECK_ERR(vfs_load(def_fn, def_file, def_size));
|
|
|
|
std::istringstream def(std::string((char*)def_file, (int)def_size));
|
|
|
|
mem_free(def_file);
|
|
|
|
|
|
|
|
std::string snd_data_fn;
|
|
|
|
float gain_percent;
|
|
|
|
def >> snd_data_fn;
|
|
|
|
def >> gain_percent;*/
|
|
|
|
|
|
|
|
float gain_percent = 100.0;
|
|
|
|
std::string snd_data_fn = def_fn;
|
|
|
|
|
|
|
|
vs->gain = gain_percent / 100.0f;
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
// store so we can shut ourselves down via snd_free when done playing
|
2004-10-05 15:11:28 +02:00
|
|
|
vs->hvs = hvs;
|
|
|
|
|
|
|
|
vs->hsd = snd_data_load(snd_data_fn.c_str(), vs->stream);
|
2004-09-19 17:57:20 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-09-21 20:43:58 +02:00
|
|
|
// open and return a handle to the sound <fn>.
|
2004-10-05 15:11:28 +02:00
|
|
|
// stream: default false
|
|
|
|
Handle snd_open(const char* const fn, const bool stream)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
return h_alloc(H_VSrc, fn, RES_UNIQUE, stream);
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// close the sound <hs> and set hs to 0.
|
2004-10-05 15:11:28 +02:00
|
|
|
int snd_free(Handle& hvs)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
return h_free(hvs, H_VSrc);
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
int snd_set_pos(Handle hvs, float x, float y, float z, bool relative /* = false */)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
H_DEREF(hvs, VSrc, vs);
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
vs->pos[0] = x; vs->pos[1] = y; vs->pos[2] = z;
|
|
|
|
vs->relative = relative;
|
|
|
|
|
|
|
|
if(vs->al_src)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
alSourcefv(vs->al_src, AL_POSITION, vs->pos);
|
|
|
|
alSourcei(vs->al_src, AL_SOURCE_RELATIVE, vs->relative);
|
2004-10-16 22:54:24 +02:00
|
|
|
al_check("snd_set_pos");
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int snd_set_gain(Handle hvs, float gain)
|
|
|
|
{
|
|
|
|
H_DEREF(hvs, VSrc, vs);
|
|
|
|
|
|
|
|
vs->gain = gain;
|
|
|
|
|
|
|
|
if(vs->al_src)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
alSourcef(vs->al_src, AL_GAIN, vs->gain);
|
2004-10-16 22:54:24 +02:00
|
|
|
al_check("snd_set_gain");
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
int snd_set_loop(Handle hvs, bool loop)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
H_DEREF(hvs, VSrc, vs);
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
vs->loop = loop;
|
|
|
|
|
|
|
|
if(vs->al_src)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
alSourcei(vs->al_src, AL_LOOPING, vs->loop);
|
2004-10-16 22:54:24 +02:00
|
|
|
al_check("snd_set_loop");
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-09-19 21:29:03 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
static float norm(const float v[3])
|
2004-10-05 15:11:28 +02:00
|
|
|
{
|
|
|
|
return v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const float MAX_DIST2 = 1000.0f;
|
|
|
|
|
|
|
|
static void vsrc_calc_cur_pri(VSrc* vs)
|
|
|
|
{
|
|
|
|
float d2; // euclidean distance to listener (squared)
|
|
|
|
if(vs->relative)
|
|
|
|
d2 = norm(vs->pos);
|
|
|
|
else
|
2004-10-16 22:54:24 +02:00
|
|
|
d2 = al_listener_dist_2(vs->pos);
|
2004-10-05 15:11:28 +02:00
|
|
|
|
|
|
|
// farther away than OpenAL cutoff - no sound contribution
|
|
|
|
if(d2 > MAX_DIST2)
|
|
|
|
{
|
|
|
|
vs->cur_pri = -1.0f;
|
|
|
|
return;
|
|
|
|
|
|
|
|
// TODO: make sure these never play, even if no sounds active
|
2004-09-19 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
// scale priority down exponentially
|
|
|
|
float e = d2 / MAX_DIST2; // 0.0f (close) .. 1.0f (far)
|
|
|
|
const float falloff = 10.0f;
|
|
|
|
vs->cur_pri = vs->static_pri * pow(falloff, e);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool vsrc_greater(const VSrc* const s1, const VSrc* const s2)
|
|
|
|
{
|
|
|
|
return s1->cur_pri > s2->cur_pri;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// list of sounds
|
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// currently active sounds - needed to update them, i.e. remove old buffers
|
|
|
|
// and enqueue just finished async buffers. this can't happen from
|
|
|
|
// io_check_complete alone - see dox there.
|
|
|
|
|
|
|
|
|
|
|
|
// sorted in ascending order of current priority
|
|
|
|
// (we remove low pri items, and have to move down everything after them,
|
|
|
|
// so they should come last)
|
|
|
|
typedef std::vector<VSrc*> VSources;
|
|
|
|
typedef VSources::iterator It;
|
|
|
|
static VSources vsources;
|
|
|
|
|
|
|
|
// don't need to sort - that's done during full update
|
|
|
|
static void list_add(VSrc* vs)
|
|
|
|
{
|
|
|
|
vsources.push_back(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void list_foreach(void(*cb)(VSrc*))
|
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
// can't use std::for_each: some entries may have been deleted
|
|
|
|
// (i.e. set to 0) since last update.
|
|
|
|
for(It it = vsources.begin(); it != vsources.end(); ++it)
|
|
|
|
{
|
|
|
|
VSrc* vs = *it;
|
|
|
|
if(vs)
|
|
|
|
cb(vs);
|
|
|
|
}
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// O(N)!
|
2004-10-15 15:18:34 +02:00
|
|
|
//
|
|
|
|
// TODO: replace with last list entry, resize -1
|
|
|
|
//
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
static void list_remove(VSrc* vs)
|
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
debug_out("list_remove vs=%p\n", vs);
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
for(size_t i = 0; i < vsources.size(); i++)
|
|
|
|
if(vsources[i] == vs)
|
2004-09-19 21:29:03 +02:00
|
|
|
{
|
2004-10-05 15:11:28 +02:00
|
|
|
vsources[i] = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
// debug_warn("list_remove: VSrc not found");
|
|
|
|
debug_out("NOT FOUND!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool vsrc_is_null(VSrc* vs)
|
|
|
|
{
|
|
|
|
return vs != 0;
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|
2004-09-19 21:29:03 +02:00
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
|
|
|
|
static int list_update()
|
|
|
|
{
|
|
|
|
// prune NULL-entries, so code below doesn't have to check if non-NULL
|
|
|
|
// (these were removed, but we didn't shuffle everything down to save time)
|
2004-10-15 15:18:34 +02:00
|
|
|
It new_end = remove_if(vsources.begin(), vsources.end(), vsrc_is_null);
|
|
|
|
vsources.erase(new_end, vsources.end());
|
2004-10-05 15:11:28 +02:00
|
|
|
|
|
|
|
// update current priorities (a function of static priority and distance)
|
|
|
|
std::for_each(vsources.begin(), vsources.end(), vsrc_calc_cur_pri);
|
|
|
|
|
|
|
|
// sort by descending current priority
|
|
|
|
std::sort(vsources.begin(), vsources.end(), vsrc_greater);
|
|
|
|
|
|
|
|
It it;
|
|
|
|
It first_unimportant = vsources.begin() + min((int)vsources.size(), al_src_cap);
|
|
|
|
|
|
|
|
// reclaim source from the less important vsources
|
|
|
|
for(it = first_unimportant; it != vsources.end(); ++it)
|
|
|
|
{
|
|
|
|
VSrc* vs = *it;
|
|
|
|
if(vs->al_src)
|
2004-10-15 15:18:34 +02:00
|
|
|
{
|
|
|
|
debug_out("reclaiming from low-pri\n");
|
2004-10-05 15:11:28 +02:00
|
|
|
vsrc_reclaim_src(vs);
|
2004-10-15 15:18:34 +02:00
|
|
|
}
|
2004-10-05 15:11:28 +02:00
|
|
|
|
|
|
|
if(!vs->loop)
|
2004-10-15 15:18:34 +02:00
|
|
|
{
|
|
|
|
debug_out("kicking out low-pri\n");
|
|
|
|
Handle tmp = vs->hvs;
|
|
|
|
snd_free(/*vs->hvs*/tmp);
|
|
|
|
}
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// grant each of the most important vsources a source
|
|
|
|
for(it = vsources.begin(); it != first_unimportant; ++it)
|
|
|
|
{
|
|
|
|
VSrc* vs = *it;
|
|
|
|
if(!vs->al_src)
|
2004-10-15 15:18:34 +02:00
|
|
|
{
|
|
|
|
debug_out("now granting high-pri a new src\n");
|
2004-10-05 15:11:28 +02:00
|
|
|
vsrc_grant_src(vs);
|
2004-10-15 15:18:34 +02:00
|
|
|
}
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::for_each(vsources.begin(), vsources.end(), vsrc_update);
|
2004-09-19 21:29:03 +02:00
|
|
|
|
2004-09-19 17:57:20 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
int snd_play(Handle hs)
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
debug_out("snd_play\n");
|
2004-10-05 15:11:28 +02:00
|
|
|
H_DEREF(hs, VSrc, vs);
|
|
|
|
|
|
|
|
list_add(vs);
|
|
|
|
|
|
|
|
// optimization (don't want to do full update here - too slow)
|
|
|
|
// either we get a source and playing begins immediately, or it'll be
|
|
|
|
// taken care of on next update
|
|
|
|
vsrc_grant_src(vs);
|
2004-09-19 17:57:20 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2004-09-19 17:57:20 +02:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// sound engine
|
|
|
|
//
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
static void vsrc_free(VSrc* vs)
|
|
|
|
{
|
|
|
|
Handle tmp = vs->hvs;
|
|
|
|
snd_free(/*vs->hvs*/tmp);
|
|
|
|
}
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-16 22:54:24 +02:00
|
|
|
int snd_update(const float pos[3], const float dir[3], const float up[3])
|
2004-09-19 17:57:20 +02:00
|
|
|
{
|
2004-10-16 22:54:24 +02:00
|
|
|
al_listener_set_pos(pos, dir, up);
|
2004-10-05 15:11:28 +02:00
|
|
|
|
|
|
|
list_update();
|
2004-09-19 17:57:20 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
// CHECK_ERR(io_check_complete());
|
2004-09-19 17:57:20 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2004-10-05 15:11:28 +02:00
|
|
|
|
2004-10-15 15:18:34 +02:00
|
|
|
void snd_shutdown()
|
2004-10-05 15:11:28 +02:00
|
|
|
{
|
2004-10-15 15:18:34 +02:00
|
|
|
debug_out("snd_shutdown\n");
|
|
|
|
list_foreach(vsrc_free);
|
|
|
|
|
|
|
|
io_buf_shutdown();
|
|
|
|
|
|
|
|
al_shutdown();
|
2004-10-05 15:11:28 +02:00
|
|
|
}
|