git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@4912 e016b1c5-ed68-44aa-a2f7-31cac88cffde
398 lines
8.0 KiB
C++
398 lines
8.0 KiB
C++
// simple OpenAL call wrappers
|
|
|
|
#include "cube.h"
|
|
|
|
#define DEBUGCOND (audiodebug==1)
|
|
|
|
VAR(al_referencedistance, 0, 400, 1000000);
|
|
VAR(al_rollofffactor, 0, 100, 1000000);
|
|
|
|
// represents an OpenAL source, an audio emitter in the 3D world
|
|
|
|
source::source() : id(0), owner(NULL), locked(false), valid(false), priority(SP_NORMAL)
|
|
{
|
|
valid = generate();
|
|
ASSERT(!valid || alIsSource(id));
|
|
}
|
|
|
|
source::~source()
|
|
{
|
|
if(valid) delete_();
|
|
}
|
|
|
|
void source::lock()
|
|
{
|
|
locked = true;
|
|
DEBUG("source locked, " << lastmillis);
|
|
}
|
|
|
|
void source::unlock()
|
|
{
|
|
locked = false;
|
|
owner = NULL;
|
|
stop();
|
|
buffer(0);
|
|
DEBUG("source unlocked, " << lastmillis);
|
|
}
|
|
|
|
void source::reset()
|
|
{
|
|
ASSERT(alIsSource(id));
|
|
owner = NULL;
|
|
locked = false;
|
|
priority = SP_NORMAL;
|
|
|
|
// restore default settings
|
|
|
|
stop();
|
|
buffer(0);
|
|
|
|
pitch(1.0f);
|
|
gain(1.0f);
|
|
position(0.0f, 0.0f, 0.0f);
|
|
velocity(0.0f, 0.0f, 0.0f);
|
|
|
|
looping(false);
|
|
sourcerelative(false);
|
|
|
|
// fit into distance model
|
|
alSourcef(id, AL_REFERENCE_DISTANCE, al_referencedistance/100.0f);
|
|
alSourcef(id, AL_ROLLOFF_FACTOR, al_rollofffactor/100.0f);
|
|
}
|
|
|
|
|
|
void source::init(sourceowner *o)
|
|
{
|
|
ASSERT(o);
|
|
owner = o;
|
|
}
|
|
|
|
void source::onreassign()
|
|
{
|
|
if(owner)
|
|
{
|
|
owner->onsourcereassign(this);
|
|
owner = NULL;
|
|
}
|
|
}
|
|
|
|
bool source::generate()
|
|
{
|
|
alclearerr();
|
|
alGenSources(1, &id);
|
|
|
|
return !alerr(false);
|
|
}
|
|
|
|
bool source::delete_()
|
|
{
|
|
alclearerr();
|
|
alDeleteSources(1, &id);
|
|
return !ALERR;
|
|
}
|
|
|
|
bool source::buffer(ALuint buf_id)
|
|
{
|
|
alclearerr();
|
|
#ifdef __APPLE__ // weird bug
|
|
if (buf_id)
|
|
#endif
|
|
alSourcei(id, AL_BUFFER, buf_id);
|
|
|
|
return !ALERR;
|
|
}
|
|
|
|
bool source::looping(bool enable)
|
|
{
|
|
alclearerr();
|
|
alSourcei(id, AL_LOOPING, enable ? 1 : 0);
|
|
return !ALERR;
|
|
}
|
|
|
|
bool source::queuebuffers(ALsizei n, const ALuint *buffer_ids)
|
|
{
|
|
alclearerr();
|
|
alSourceQueueBuffers(id, n, buffer_ids);
|
|
return !ALERR;
|
|
}
|
|
|
|
bool source::unqueueallbuffers()
|
|
{
|
|
alclearerr();
|
|
ALint queued;
|
|
alGetSourcei(id, AL_BUFFERS_QUEUED, &queued);
|
|
ALERR;
|
|
loopi(queued)
|
|
{
|
|
ALuint buffer;
|
|
alSourceUnqueueBuffers(id, 1, &buffer);
|
|
}
|
|
return !ALERR;
|
|
}
|
|
|
|
bool source::gain(float g)
|
|
{
|
|
alclearerr();
|
|
alSourcef(id, AL_GAIN, g);
|
|
return !ALERR;
|
|
}
|
|
|
|
bool source::pitch(float p)
|
|
{
|
|
alclearerr();
|
|
alSourcef(id, AL_PITCH, p);
|
|
return !ALERR;
|
|
}
|
|
|
|
bool source::position(const vec &pos)
|
|
{
|
|
return position(pos.x, pos.y, pos.z);
|
|
}
|
|
|
|
bool source::position(float x, float y, float z)
|
|
{
|
|
alclearerr();
|
|
alSource3f(id, AL_POSITION, x, y, z);
|
|
return !ALERR;
|
|
}
|
|
|
|
bool source::velocity(float x, float y, float z)
|
|
{
|
|
alclearerr();
|
|
alSource3f(id, AL_VELOCITY, x, y, z);
|
|
return !ALERR;
|
|
}
|
|
|
|
vec source::position()
|
|
{
|
|
alclearerr();
|
|
ALfloat v[3];
|
|
alGetSourcefv(id, AL_POSITION, v);
|
|
if(ALERR) return vec(0,0,0);
|
|
else return vec(v[0], v[1], v[2]);
|
|
}
|
|
|
|
bool source::sourcerelative(bool enable)
|
|
{
|
|
alclearerr();
|
|
alSourcei(id, AL_SOURCE_RELATIVE, enable ? AL_TRUE : AL_FALSE);
|
|
return !ALERR;
|
|
}
|
|
|
|
int source::state()
|
|
{
|
|
ALint s;
|
|
alGetSourcei(id, AL_SOURCE_STATE, &s);
|
|
return s;
|
|
}
|
|
|
|
bool source::secoffset(float secs)
|
|
{
|
|
alclearerr();
|
|
alSourcef(id, AL_SEC_OFFSET, secs);
|
|
// some openal implementations seem to spam invalid enum on this
|
|
// return !ALERR;
|
|
return !alerr(false);
|
|
}
|
|
|
|
float source::secoffset()
|
|
{
|
|
alclearerr();
|
|
ALfloat s;
|
|
alGetSourcef(id, AL_SEC_OFFSET, &s);
|
|
// some openal implementations seem to spam invalid enum on this
|
|
// ALERR;
|
|
alerr(false);
|
|
return s;
|
|
}
|
|
|
|
bool source::playing()
|
|
{
|
|
return (state() == AL_PLAYING);
|
|
}
|
|
|
|
bool source::play()
|
|
{
|
|
alclearerr();
|
|
alSourcePlay(id);
|
|
return !ALERR;
|
|
}
|
|
|
|
bool source::stop()
|
|
{
|
|
alclearerr();
|
|
alSourceStop(id);
|
|
return !ALERR;
|
|
}
|
|
|
|
bool source::rewind()
|
|
{
|
|
alclearerr();
|
|
alSourceRewind(id);
|
|
return !ALERR;
|
|
}
|
|
|
|
void source::printposition()
|
|
{
|
|
alclearerr();
|
|
vec v = position();
|
|
ALint s;
|
|
alGetSourcei(id, AL_SOURCE_TYPE, &s);
|
|
conoutf("sound %d: pos(%f,%f,%f) t(%d) ", id, v.x, v.y, v.z, s);
|
|
ALERR;
|
|
}
|
|
|
|
|
|
// represents an OpenAL sound buffer
|
|
|
|
sbuffer::sbuffer() : id(0), name(NULL)
|
|
{
|
|
}
|
|
|
|
sbuffer::~sbuffer()
|
|
{
|
|
unload();
|
|
}
|
|
|
|
bool sbuffer::load()
|
|
{
|
|
if(!name) return false;
|
|
if(id) return true;
|
|
alclearerr();
|
|
alGenBuffers(1, &id);
|
|
if(!ALERR)
|
|
{
|
|
const char *exts[] = { "", ".wav", ".ogg" };
|
|
string filepath;
|
|
loopi(sizeof(exts)/sizeof(exts[0]))
|
|
{
|
|
formatstring(filepath)("packages/audio/sounds/%s%s", name, exts[i]);
|
|
stream *f = openfile(path(filepath), "rb");
|
|
if(!f) continue;
|
|
|
|
size_t len = strlen(filepath);
|
|
if(len >= 4 && !strcasecmp(filepath + len - 4, ".ogg"))
|
|
{
|
|
OggVorbis_File oggfile;
|
|
if(!ov_open_callbacks(f, &oggfile, NULL, 0, oggcallbacks))
|
|
{
|
|
vorbis_info *info = ov_info(&oggfile, -1);
|
|
|
|
const size_t BUFSIZE = 32*1024;
|
|
vector<char> buf;
|
|
int bitstream;
|
|
long bytes;
|
|
|
|
do
|
|
{
|
|
char buffer[BUFSIZE];
|
|
bytes = ov_read(&oggfile, buffer, BUFSIZE, isbigendian(), 2, 1, &bitstream);
|
|
loopi(bytes) buf.add(buffer[i]);
|
|
} while(bytes > 0);
|
|
|
|
alBufferData(id, info->channels == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16, buf.getbuf(), buf.length(), info->rate);
|
|
ov_clear(&oggfile);
|
|
}
|
|
else
|
|
{
|
|
delete f;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SDL_AudioSpec wavspec;
|
|
uint32_t wavlen;
|
|
uint8_t *wavbuf;
|
|
|
|
if(!SDL_LoadWAV_RW(f->rwops(), 1, &wavspec, &wavbuf, &wavlen))
|
|
{
|
|
SDL_ClearError();
|
|
continue;
|
|
}
|
|
|
|
ALenum format;
|
|
switch(wavspec.format) // map wav header to openal format
|
|
{
|
|
case AUDIO_U8:
|
|
case AUDIO_S8:
|
|
format = wavspec.channels==2 ? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8;
|
|
break;
|
|
case AUDIO_U16:
|
|
case AUDIO_S16:
|
|
format = wavspec.channels==2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
|
|
break;
|
|
default:
|
|
SDL_FreeWAV(wavbuf);
|
|
delete f;
|
|
unload();
|
|
return false;
|
|
}
|
|
|
|
alBufferData(id, format, wavbuf, wavlen, wavspec.freq);
|
|
SDL_FreeWAV(wavbuf);
|
|
delete f;
|
|
|
|
if(ALERR) break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
unload(); // loading failed
|
|
return false;
|
|
}
|
|
|
|
void sbuffer::unload()
|
|
{
|
|
if(!id) return;
|
|
alclearerr();
|
|
if(alIsBuffer(id)) alDeleteBuffers(1, &id);
|
|
id = 0;
|
|
ALERR;
|
|
}
|
|
|
|
// buffer collection, find or load data
|
|
|
|
bufferhashtable::~bufferhashtable() {}
|
|
|
|
sbuffer *bufferhashtable::find(char *name)
|
|
{
|
|
sbuffer *b = access(name);
|
|
if(!b)
|
|
{
|
|
name = newstring(name);
|
|
b = &(*this)[name];
|
|
b->name = name;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
// OpenAL error handling
|
|
|
|
void alclearerr()
|
|
{
|
|
alGetError();
|
|
}
|
|
|
|
bool alerr(bool msg, int line)
|
|
{
|
|
ALenum er = alGetError();
|
|
if(er && msg)
|
|
{
|
|
const char *desc = "unknown";
|
|
switch(er)
|
|
{
|
|
case AL_INVALID_NAME: desc = "invalid name"; break;
|
|
case AL_INVALID_ENUM: desc = "invalid enum"; break;
|
|
case AL_INVALID_VALUE: desc = "invalid value"; break;
|
|
case AL_INVALID_OPERATION: desc = "invalid operation"; break;
|
|
case AL_OUT_OF_MEMORY: desc = "out of memory"; break;
|
|
}
|
|
if(line) conoutf("\f3OpenAL Error (%X): %s, line %d", er, desc, line);
|
|
else conoutf("\f3OpenAL Error (%X): %s", er, desc);
|
|
}
|
|
return er > 0;
|
|
}
|