// simple OpenAL call wrappers #include "pch.h" #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; } void source::unlock() { locked = false; owner = NULL; stop(); buffer(0); } 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) { alclearerr(); alSourcefv(id, AL_POSITION, (ALfloat *) &pos); return !ALERR; } 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(); vec p; alGetSourcefv(id, AL_POSITION, (ALfloat*) &p); if(ALERR) return vec(0,0,0); else return p; } 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])) { s_sprintf(filepath)("packages/audio/sounds/%s%s", name, exts[i]); const char *file = findfile(path(filepath), "rb"); size_t len = strlen(filepath); if(len >= 4 && !strcasecmp(filepath + len - 4, ".ogg")) { FILE *f = fopen(file, "rb"); if(!f) continue; 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 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 { fclose(f); continue; } } else { SDL_AudioSpec wavspec; uint32_t wavlen; uint8_t *wavbuf; if(!SDL_LoadWAV(file, &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); unload(); return false; } alBufferData(id, format, wavbuf, wavlen, wavspec.freq); SDL_FreeWAV(wavbuf); 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; }