From c02ea1ce838fd3b7390bfbf1a9041b129380fa5a Mon Sep 17 00:00:00 2001 From: fgenesis Date: Sun, 27 May 2012 04:46:36 +0200 Subject: [PATCH] Re-implement (optionally) pre-decoding audio to reduce disk seek/decoding time. This was removed in HG changeset 7ec478d993b7, and is now implemented in a way that is better than before: voice overs and music no longer cause decoding lag, as they are always decoded on-the-fly. The additional memory use (~40 MB) should be no problem for anyone. The default is still to decode everything on the fly. --- Aquaria/DSQ.cpp | 1 + Aquaria/UserSettings.cpp | 15 +++- Aquaria/UserSettings.h | 3 +- BBGE/Core.h | 5 +- BBGE/FmodOpenALBridge.cpp | 141 ++++++++++++++++++++++++++++++++++---- 5 files changed, 148 insertions(+), 17 deletions(-) diff --git a/Aquaria/DSQ.cpp b/Aquaria/DSQ.cpp index e3e60f0..b1e444b 100644 --- a/Aquaria/DSQ.cpp +++ b/Aquaria/DSQ.cpp @@ -4484,6 +4484,7 @@ void DSQ::onUpdate(float dt) os << " | p: " << core->processedRenderObjectCount << " | t: " << core->totalRenderObjectCount; os << " | s: " << dsq->continuity.seconds; os << " | evQ: " << core->eventQueue.getSize(); + os << " | sndQ: " << core->dbg_numThreadDecoders; /* os << " | s: " << dsq->continuity.seconds; os << " cr: " << core->cullRadius; diff --git a/Aquaria/UserSettings.cpp b/Aquaria/UserSettings.cpp index 1362687..3db6155 100644 --- a/Aquaria/UserSettings.cpp +++ b/Aquaria/UserSettings.cpp @@ -87,12 +87,17 @@ void UserSettings::save() } xml_audio.InsertEndChild(xml_volume); - TiXmlElement xml_device("Device"); { xml_device.SetAttribute("name", audio.deviceName); } xml_audio.InsertEndChild(xml_device); + + TiXmlElement xml_prebuf("Prebuffer"); + { + xml_prebuf.SetAttribute("on", audio.prebuffer); + } + xml_audio.InsertEndChild(xml_prebuf); } doc.InsertEndChild(xml_audio); @@ -393,6 +398,12 @@ void UserSettings::load(bool doApply, const std::string &overrideFile) { audio.deviceName = xml_device->Attribute("name"); } + + TiXmlElement *xml_prebuf = xml_audio->FirstChildElement("Prebuffer"); + if (xml_prebuf) + { + xml_prebuf->Attribute("on", &audio.prebuffer); + } } TiXmlElement *xml_video = doc.FirstChildElement("Video"); if (xml_video) @@ -547,6 +558,8 @@ void UserSettings::apply() } dsq->bindInput(); + + core->settings.prebufferSounds = audio.prebuffer; #endif } diff --git a/Aquaria/UserSettings.h b/Aquaria/UserSettings.h index d1fd4b9..6d877b8 100644 --- a/Aquaria/UserSettings.h +++ b/Aquaria/UserSettings.h @@ -85,12 +85,13 @@ public: struct Audio { - Audio() { micOn = 0; octave=0; musvol=voxvol=sfxvol=1.0; subtitles=false; } + Audio() { micOn = 0; octave=0; musvol=voxvol=sfxvol=1.0; subtitles=false; prebuffer=false;} int micOn; int octave; float voxvol, sfxvol, musvol; int subtitles; std::string deviceName; + int prebuffer; } audio; struct Video diff --git a/BBGE/Core.h b/BBGE/Core.h index 90759bb..76f6738 100644 --- a/BBGE/Core.h +++ b/BBGE/Core.h @@ -77,10 +77,11 @@ struct ScreenMode struct CoreSettings { - CoreSettings() { renderOn = true; updateOn = true; runInBackground = false; } + CoreSettings() { renderOn = true; updateOn = true; runInBackground = false; prebufferSounds = false; } bool renderOn; bool runInBackground; bool updateOn; // NOT IMPLEMENTED YET + bool prebufferSounds; }; enum CoreFlags @@ -1306,6 +1307,8 @@ public: int tgaSave(const char *filename, short int width, short int height, unsigned char pixelDepth, unsigned char *imageData); + volatile int dbg_numThreadDecoders; + protected: std::string fpsDebugString; diff --git a/BBGE/FmodOpenALBridge.cpp b/BBGE/FmodOpenALBridge.cpp index cb33f9a..da6dcad 100644 --- a/BBGE/FmodOpenALBridge.cpp +++ b/BBGE/FmodOpenALBridge.cpp @@ -250,6 +250,8 @@ void OggDecoder::decode_loop(OggDecoder *this_) decoderList.erase(it++); } } + + core->dbg_numThreadDecoders = decoderList.size(); } } @@ -584,14 +586,17 @@ namespace FMOD { class OpenALSound { public: - OpenALSound(FILE *_fp, const bool _looping); - OpenALSound(void *_data, long _size, const bool _looping); + OpenALSound(FILE *_fp, const bool _looping); // ctor for ogg streamed from file + OpenALSound(void *_data, long _size, const bool _looping); // ctor for ogg streamed from memory + OpenALSound(ALuint _bid, const bool _looping); // ctor for raw samples already assigned an opanAL buffer ID FILE *getFile() const { return fp; } const void *getData() const { return data; } long getSize() const { return size; } bool isLooping() const { return looping; } + bool isRaw() const { return raw; } FMOD_RESULT release(); void reference() { refcount++; } + ALuint getBufferName() const { return bid; } private: FILE * const fp; @@ -599,6 +604,8 @@ private: const long size; // Only used if fp==NULL const bool looping; int refcount; + const bool raw; // true if buffer holds raw PCM data + ALuint bid; // only used if raw == true }; OpenALSound::OpenALSound(FILE *_fp, const bool _looping) @@ -607,6 +614,8 @@ OpenALSound::OpenALSound(FILE *_fp, const bool _looping) , size(0) , looping(_looping) , refcount(1) + , raw(false) + , bid(0) { } @@ -616,6 +625,19 @@ OpenALSound::OpenALSound(void *_data, long _size, const bool _looping) , size(_size) , looping(_looping) , refcount(1) + , raw(false) + , bid(0) +{ +} + +OpenALSound::OpenALSound(ALuint _bid, const bool _looping) + : fp(NULL) + , data(NULL) + , size(0) + , looping(_looping) + , refcount(1) + , raw(true) + , bid(_bid) { } @@ -625,10 +647,17 @@ FMOD_RESULT OpenALSound::release() refcount--; if (refcount <= 0) { - if (fp) - fclose(fp); - else - free(data); + if(raw) + { + alDeleteBuffers(1, &bid); + } + else + { + if (fp) + fclose(fp); + else + free(data); + } delete this; } return FMOD_OK; @@ -738,15 +767,23 @@ bool OpenALChannel::start(OpenALSound *sound) { if (decoder) delete decoder; - if (sound->getFile()) - decoder = new OggDecoder(sound->getFile()); - else - decoder = new OggDecoder(sound->getData(), sound->getSize()); - if (!decoder->start(sid, sound->isLooping())) + if (sound->isRaw()) { - delete decoder; - decoder = NULL; - return false; + alSourcei(sid, AL_BUFFER, sound->getBufferName()); + alSourcei(sid, AL_LOOPING, sound->isLooping() ? AL_TRUE : AL_FALSE); + } + else + { + if (sound->getFile()) + decoder = new OggDecoder(sound->getFile()); + else + decoder = new OggDecoder(sound->getData(), sound->getSize()); + if (!decoder->start(sid, sound->isLooping())) + { + delete decoder; + decoder = NULL; + return false; + } } return true; } @@ -1014,6 +1051,9 @@ FMOD_RESULT DSP::setParameter(int index, float value) void OpenALChannel::setSound(OpenALSound *_sound) { + if(sound == _sound) + return; + if (sound) sound->release(); @@ -1115,6 +1155,58 @@ FMOD_RESULT OpenALSystem::createDSPByType(const FMOD_DSP_TYPE type, DSP **dsp) return FMOD_ERR_INTERNAL; } +static void *decode_to_pcm(FILE *io, ALenum &format, ALsizei &size, ALuint &freq) +{ + ALubyte *retval = NULL; + + // Uncompress and feed to the AL. + OggVorbis_File vf; + memset(&vf, '\0', sizeof (vf)); + if (ov_open(io, &vf, NULL, 0) == 0) + { + int bitstream = 0; + vorbis_info *info = ov_info(&vf, -1); + size = 0; + format = (info->channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; + freq = info->rate; + + if ((info->channels != 1) && (info->channels != 2)) + { + ov_clear(&vf); + return NULL; + } + + char buf[1024 * 16]; + long rc = 0; + size_t allocated = 64 * 1024; + retval = (ALubyte *) malloc(allocated); + while ( (rc = ov_read(&vf, buf, sizeof (buf), BBGE_BIGENDIAN, 2, 1, &bitstream)) != 0 ) + { + if (rc > 0) + { + size += rc; + if (size >= allocated) + { + allocated *= 2; + ALubyte *tmp = (ALubyte *) realloc(retval, allocated); + if (tmp == NULL) + { + free(retval); + retval = NULL; + break; + } + retval = tmp; + } + memcpy(retval + (size - rc), buf, rc); + } + } + ov_clear(&vf); + return retval; + } + return NULL; +} + + ALBRIDGE(System,createSound,(const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, Sound **sound),(name_or_data,mode,exinfo,sound)) FMOD_RESULT OpenALSystem::createSound(const char *name_or_data, const FMOD_MODE mode, const FMOD_CREATESOUNDEXINFO *exinfo, Sound **sound) { @@ -1137,11 +1229,32 @@ FMOD_RESULT OpenALSystem::createSound(const char *name_or_data, const FMOD_MODE if (mode & FMOD_CREATESTREAM) { + // Create streaming file handle decoder *sound = (Sound *) new OpenALSound(io, (((mode & FMOD_LOOP_OFF) == 0) && (mode & FMOD_LOOP_NORMAL))); retval = FMOD_OK; } + else if(core->settings.prebufferSounds) + { + // Pre-decode the sound file and store the raw PCM buffer + ALenum format = AL_NONE; + ALsizei size = 0; + ALuint freq = 0; + void *data = decode_to_pcm(io, format, size, freq); + fclose(io); + + ALuint bid = 0; + alGenBuffers(1, &bid); + if (bid != 0) + { + alBufferData(bid, format, data, size, freq); + *sound = (Sound *) new OpenALSound(bid, (((mode & FMOD_LOOP_OFF) == 0) && (mode & FMOD_LOOP_NORMAL))); + retval = FMOD_OK; + } + free(data); + } else { + // Create streaming memory decoder fseek(io, 0, SEEK_END); long size = ftell(io); if (fseek(io, 0, SEEK_SET) != 0)