Files
assaultcube-/source/src/openal.cpp
T
ac-stef d0b309320b really cleanup sound
- collect most sound infos in one table and use it to generate the menus
- no more sound category checks with ranges
- now only PAINx and FOOTSTEP* sounds use hardcoded index numbers

enumsounds <list of categories>

  Lists sound index numbers and short descriptions for all sounds of one
  or more categories. The argument should contain one or more of PAIN,
  OWNPAIN, WEAPON, PICKUP, MOVEMENT, BULLET, OTHER, VOICECOM, TEAM,
  PUBLIC, FFA, FLAGONLY. All sounds with at least one of the flags will
  be listed. Flag names prefixed with "!" will exclude all matching
  sound from the list.

  Example: /echo (enumsounds "VOICECOM !PUBLIC") will list all voicecom
  sounds that are for teams only.

DOCREF enumsounds
2017-05-22 17:30:51 +02:00

410 lines
8.9 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 !ALERRF("gain: %f", g);
}
bool source::pitch(float p)
{
alclearerr();
alSourcef(id, AL_PITCH, p);
return !ALERRF("pitch: %f", p);
}
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);
if(x < 0 || y < 0 || z < -128 || x > ssize || y > ssize || z > 128) clientlogf("warning: sound position out of range (%f,%f,%f)", x, y, z);
return !ALERRF("id %u, %d, x: %f, y: %f, z: %f", id, alIsSource(id) == AL_TRUE ? 1 : 0, x, y, z);
// return !ALERRF("x: %f, y: %f, z: %f", x, y, z); // when sound bug has been absent for a while, switch to this version
}
bool source::velocity(float x, float y, float z)
{
alclearerr();
alSource3f(id, AL_VELOCITY, x, y, z);
return !ALERRF("dx: %f, dy: %f, dz: %f", x, y, z);
}
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(bool trydl)
{
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/%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)
{
unload();
return false;
};
}
return true;
}
if(trydl) requirepackage(PCK_AUDIO, name); // only try donwloading after trying all extensions
}
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(const 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, const char *s, ...)
{
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(s)
{
defvformatstring(p, s, s);
conoutf("\f3OpenAL Error (%X): %s, line %d, (%s)", er, desc, line, p);
}
else if(line) conoutf("\f3OpenAL Error (%X): %s, line %d", er, desc, line);
else conoutf("\f3OpenAL Error (%X): %s", er, desc);
}
return er > 0;
}