diff --git a/apps/openmw/mwsound/openal_output.cpp b/apps/openmw/mwsound/openal_output.cpp index 3f9910038..45be36687 100644 --- a/apps/openmw/mwsound/openal_output.cpp +++ b/apps/openmw/mwsound/openal_output.cpp @@ -1,7 +1,11 @@ #include #include +#include #include "openal_output.hpp" +#include "sound_decoder.hpp" +#include "sound.hpp" + namespace MWSound { @@ -9,6 +13,176 @@ namespace MWSound static void fail(const std::string &msg) { throw std::runtime_error("OpenAL exception: " + msg); } +static void throwALerror() +{ + ALenum err = alGetError(); + if(err != AL_NO_ERROR) + fail(alGetString(err)); +} + + +static ALenum getALFormat(Sound_Decoder::ChannelConfig chans, Sound_Decoder::SampleType type) +{ + if(chans == Sound_Decoder::MonoChannels) + { + if(type == Sound_Decoder::Int16Sample) + return AL_FORMAT_MONO16; + else if(type == Sound_Decoder::UInt8Sample) + return AL_FORMAT_MONO8; + else + fail("Unsupported sample type"); + } + else if(chans == Sound_Decoder::StereoChannels) + { + if(type == Sound_Decoder::Int16Sample) + return AL_FORMAT_STEREO16; + else if(type == Sound_Decoder::UInt8Sample) + return AL_FORMAT_STEREO8; + else + fail("Unsupported sample type"); + } + else + fail("Unsupported channel config"); + return AL_NONE; +} + + +class OpenAL_SoundStream : public Sound +{ + // This should be something sane, like 4, but currently cell loads tend to + // cause the stream to underrun + static const ALuint NumBuffers = 150; + static const ALuint BufferSize = 32768; + + ALuint Source; + ALuint Buffers[NumBuffers]; + + ALenum Format; + ALsizei SampleRate; + + std::auto_ptr Decoder; + +public: + OpenAL_SoundStream(std::auto_ptr decoder) : Decoder(decoder) + { + throwALerror(); + + alGenSources(1, &Source); + throwALerror(); + try + { + alGenBuffers(NumBuffers, Buffers); + throwALerror(); + } + catch(std::exception &e) + { + alDeleteSources(1, &Source); + alGetError(); + throw; + } + + try + { + int srate; + enum Sound_Decoder::ChannelConfig chans; + enum Sound_Decoder::SampleType type; + + Decoder->GetInfo(&srate, &chans, &type); + Format = getALFormat(chans, type); + SampleRate = srate; + } + catch(std::exception &e) + { + alDeleteSources(1, &Source); + alDeleteBuffers(NumBuffers, Buffers); + alGetError(); + throw; + } + } + virtual ~OpenAL_SoundStream() + { + alDeleteSources(1, &Source); + alDeleteBuffers(NumBuffers, Buffers); + alGetError(); + Decoder->Close(); + } + + virtual bool Play() + { + std::vector data(BufferSize); + + alSourceStop(Source); + alSourcei(Source, AL_BUFFER, 0); + throwALerror(); + + for(ALuint i = 0;i < NumBuffers;i++) + { + size_t got; + got = Decoder->Read(&data[0], data.size()); + alBufferData(Buffers[i], Format, &data[0], got, SampleRate); + } + throwALerror(); + + alSourceQueueBuffers(Source, NumBuffers, Buffers); + alSourcePlay(Source); + throwALerror(); + + return true; + } + + virtual void Stop() + { + alSourceStop(Source); + alSourcei(Source, AL_BUFFER, 0); + throwALerror(); + // FIXME: Rewind decoder + } + + virtual bool isPlaying() + { + ALint processed, state; + + alGetSourcei(Source, AL_SOURCE_STATE, &state); + alGetSourcei(Source, AL_BUFFERS_PROCESSED, &processed); + throwALerror(); + + if(processed > 0) + { + std::vector data(BufferSize); + do { + ALuint bufid; + size_t got; + + alSourceUnqueueBuffers(Source, 1, &bufid); + processed--; + + got = Decoder->Read(&data[0], data.size()); + if(got > 0) + { + alBufferData(bufid, Format, &data[0], got, SampleRate); + alSourceQueueBuffers(Source, 1, &bufid); + } + } while(processed > 0); + throwALerror(); + } + + if(state != AL_PLAYING && state != AL_PAUSED) + { + ALint queued; + + alGetSourcei(Source, AL_BUFFERS_QUEUED, &queued); + throwALerror(); + if(queued == 0) + return false; + + alSourcePlay(Source); + throwALerror(); + } + + return true; + } +}; + bool OpenAL_Output::Initialize(const std::string &devname) { @@ -50,6 +224,20 @@ void OpenAL_Output::Deinitialize() } +Sound* OpenAL_Output::StreamSound(const std::string &fname, std::auto_ptr decoder) +{ + std::auto_ptr sound; + + if(!decoder->Open(fname)) + return NULL; + + sound.reset(new OpenAL_SoundStream(decoder)); + sound->Play(); + + return sound.release(); +} + + OpenAL_Output::OpenAL_Output(SoundManager &mgr) : Sound_Output(mgr), Device(0), Context(0) { diff --git a/apps/openmw/mwsound/openal_output.hpp b/apps/openmw/mwsound/openal_output.hpp index d5748ee30..b0cd469e5 100644 --- a/apps/openmw/mwsound/openal_output.hpp +++ b/apps/openmw/mwsound/openal_output.hpp @@ -11,6 +11,8 @@ namespace MWSound { class SoundManager; + class Sound_Decoder; + class Sound; class OpenAL_Output : public Sound_Output { @@ -20,6 +22,8 @@ namespace MWSound virtual bool Initialize(const std::string &devname=""); virtual void Deinitialize(); + virtual Sound *StreamSound(const std::string &fname, std::auto_ptr decoder); + OpenAL_Output(SoundManager &mgr); virtual ~OpenAL_Output(); diff --git a/apps/openmw/mwsound/sound_output.hpp b/apps/openmw/mwsound/sound_output.hpp index b36a4e89f..16639de7b 100644 --- a/apps/openmw/mwsound/sound_output.hpp +++ b/apps/openmw/mwsound/sound_output.hpp @@ -2,10 +2,13 @@ #define GAME_SOUND_SOUND_OUTPUT_H #include +#include namespace MWSound { class SoundManager; + class Sound_Decoder; + class Sound; class Sound_Output { @@ -14,6 +17,8 @@ namespace MWSound virtual bool Initialize(const std::string &devname="") = 0; virtual void Deinitialize() = 0; + virtual Sound *StreamSound(const std::string &fname, std::auto_ptr decoder) = 0; + Sound_Output(SoundManager &mgr) : mgr(mgr) { } public: virtual ~Sound_Output() { } diff --git a/apps/openmw/mwsound/soundmanager.cpp b/apps/openmw/mwsound/soundmanager.cpp index d231f1d4a..9ba5479d7 100644 --- a/apps/openmw/mwsound/soundmanager.cpp +++ b/apps/openmw/mwsound/soundmanager.cpp @@ -155,7 +155,7 @@ namespace MWSound if(mMusic) mMusic->Stop(); std::auto_ptr decoder(new DEFAULT_DECODER); - //mMusic.reset(Output->StreamSound(filename, decoder)); + mMusic.reset(Output->StreamSound(filename, decoder)); } void SoundManager::streamMusic(const std::string& filename)