//////////////////////////////////////////////////////////////////////////////// // Scorched3D (c) 2000-2004 // // This file is part of Scorched3D. // // Scorched3D is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // Scorched3D is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Scorched3D; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //////////////////////////////////////////////////////////////////////////////// #include #include #include #ifdef HAVE_OGG #define BUFFER_SIZE (4096 * 50) SoundBufferDynamicOVSourceInstance::SoundBufferDynamicOVSourceInstance( unsigned int source, const char *fileName) : SoundBufferSourceInstance(source) { ov_callbacks callbacks = { read_func, seek_func, close_func, tell_func }; FILE *oggFile = fopen(fileName, "rb"); if (!oggFile) { dialogMessage("OGG Vorbis", formatString("Could not open ogg file \"%s\"", fileName)); return; } int result = ov_open_callbacks((void *) oggFile, &oggStream_, 0, 0, callbacks); if(result < 0) { fclose(oggFile); dialogMessage("OGG Vorbis", formatString("Could not open ogg stream \"%s\" : %s", fileName, errorString(result))); return; } vorbisInfo_ = ov_info(&oggStream_, -1); if(vorbisInfo_->channels == 1) format_ = AL_FORMAT_MONO16; else format_ = AL_FORMAT_STEREO16; alGenBuffers(2, buffers_); if (alGetError() != AL_NO_ERROR) return; } SoundBufferDynamicOVSourceInstance::~SoundBufferDynamicOVSourceInstance() { if (buffers_[0]) alDeleteBuffers(2, buffers_); ov_clear(&oggStream_); } void SoundBufferDynamicOVSourceInstance::play(bool repeat) { ov_raw_seek(&oggStream_, 0); if (!addDataToBuffer(buffers_[0], repeat)) return; if (!addDataToBuffer(buffers_[1], repeat)) return; alSourcei(source_, AL_BUFFER, 0); // Reset the source alSourcei(source_, AL_LOOPING, AL_FALSE); int error = 0; alSourceQueueBuffers(source_, 2, buffers_); if ((error = alGetError()) != AL_NO_ERROR) return; alSourcePlay(source_); if ((error = alGetError()) != AL_NO_ERROR) return; } void SoundBufferDynamicOVSourceInstance::stop() { int queued = 2; while(queued--) { ALuint buffer; alSourceUnqueueBuffers(source_, 1, &buffer); } } void SoundBufferDynamicOVSourceInstance::simulate(bool repeat) { int processed = 0; alGetSourcei(source_, AL_BUFFERS_PROCESSED, &processed); if (processed == 2) { Logger::log("Warning: OGG falling behind"); } while(processed--) { //Logger::log(formatString("Processed %u %i", source_, processed)); ALuint buffer; alSourceUnqueueBuffers(source_, 1, &buffer); if (addDataToBuffer(buffer, repeat)) { alSourceQueueBuffers(source_, 1, &buffer); } } } bool SoundBufferDynamicOVSourceInstance::addDataToBuffer(unsigned int buffer, bool loop) { char data[BUFFER_SIZE]; int size = 0; int section; int result; while(size < BUFFER_SIZE) { result = ov_read(&oggStream_, data + size, BUFFER_SIZE - size, 0, 2, 1, §ion); if (result > 0) size += result; else if (result < 0) return false; // Error else break; } if(size == 0) { if (!loop) return false; else { ov_raw_seek(&oggStream_, 0); return addDataToBuffer(buffer, false); } } alBufferData(buffer, format_, data, size, vorbisInfo_->rate); return true; } const char *SoundBufferDynamicOVSourceInstance::errorString(int code) { switch(code) { case OV_EREAD: return ("Read from media."); case OV_ENOTVORBIS: return ("Not Vorbis data."); case OV_EVERSION: return ("Vorbis version mismatch."); case OV_EBADHEADER: return ("Invalid Vorbis header."); case OV_EFAULT: return ("Internal logic fault (bug or heap/stack corruption."); default: return ("Unknown Ogg error."); } } size_t SoundBufferDynamicOVSourceInstance::read_func(void *ptr, size_t size, size_t nmemb, void *datasource) { return fread(ptr, size, nmemb, (FILE *) datasource); } int SoundBufferDynamicOVSourceInstance::seek_func(void *datasource, ogg_int64_t offset, int whence) { return fseek((FILE*) datasource, (long) offset, whence); } int SoundBufferDynamicOVSourceInstance::close_func(void *datasource) { return fclose((FILE *) datasource); } long SoundBufferDynamicOVSourceInstance::tell_func(void *datasource) { return ftell((FILE *) datasource); } SoundBufferDynamicOV::SoundBufferDynamicOV(const char *fileName) : SoundBuffer(fileName) { } SoundBufferDynamicOV::~SoundBufferDynamicOV() { } SoundBufferSourceInstance *SoundBufferDynamicOV::createSourceInstance(unsigned int source) { return new SoundBufferDynamicOVSourceInstance(source, fileName_.c_str()); } #endif // HAVE_OGG