1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-24 00:40:01 +00:00

Don't be so throw-happy in the sound manager

This commit is contained in:
Chris Robinson 2017-09-14 03:41:11 -07:00
parent abe80f5868
commit c17edfd547
4 changed files with 373 additions and 377 deletions

View file

@ -39,6 +39,26 @@ const float UnitsPerMeter = 69.99125109f;
const int sLoudnessFPS = 20; // loudness values per second of audio const int sLoudnessFPS = 20; // loudness values per second of audio
ALCenum checkALCError(ALCdevice *device, const char *func, int line)
{
ALCenum err = alcGetError(device);
if(err != ALC_NO_ERROR)
std::cerr<< ">>>>>>>>> ALC error "<<alcGetString(device, err)<<" ("<<err<<") @ "<<
func<<":"<<line <<std::endl;
return err;
}
#define getALCError(d) checkALCError((d), __FUNCTION__, __LINE__)
ALenum checkALError(const char *func, int line)
{
ALenum err = alGetError();
if(err != AL_NO_ERROR)
std::cerr<< ">>>>>>>>> AL error "<<alGetString(err)<<" ("<<err<<") @ "<<
func<<":"<<line <<std::endl;
return err;
}
#define getALError() checkALError(__FUNCTION__, __LINE__)
// Helper to get an OpenAL extension function // Helper to get an OpenAL extension function
template<typename T, typename R> template<typename T, typename R>
void convertPointer(T& dest, R src) void convertPointer(T& dest, R src)
@ -144,7 +164,7 @@ void LoadEffect(ALuint effect, const EFXEAXREVERBPROPERTIES &props)
alEffectf(effect, AL_REVERB_ROOM_ROLLOFF_FACTOR, props.flRoomRolloffFactor); alEffectf(effect, AL_REVERB_ROOM_ROLLOFF_FACTOR, props.flRoomRolloffFactor);
alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, props.iDecayHFLimit ? AL_TRUE : AL_FALSE); alEffecti(effect, AL_REVERB_DECAY_HFLIMIT, props.iDecayHFLimit ? AL_TRUE : AL_FALSE);
} }
alGetError(); getALError();
} }
} }
@ -152,30 +172,6 @@ void LoadEffect(ALuint effect, const EFXEAXREVERBPROPERTIES &props)
namespace MWSound namespace MWSound
{ {
static void fail(const std::string &msg)
{ throw std::runtime_error("OpenAL exception: " + msg); }
static void throwALCerror(ALCdevice *device)
{
ALCenum err = alcGetError(device);
if(err != ALC_NO_ERROR)
{
const ALCchar *errstring = alcGetString(device, err);
fail(errstring ? errstring : "");
}
}
static void throwALerror()
{
ALenum err = alGetError();
if(err != AL_NO_ERROR)
{
const ALchar *errstring = alGetString(err);
fail(errstring ? errstring : "");
}
}
static ALenum getALFormat(ChannelConfig chans, SampleType type) static ALenum getALFormat(ChannelConfig chans, SampleType type)
{ {
static const struct { static const struct {
@ -268,7 +264,8 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type)
} }
} }
fail(std::string("Unsupported sound format (")+getChannelConfigName(chans)+", "+getSampleTypeName(type)+")"); std::cerr<< "Unsupported sound format ("<<getChannelConfigName(chans)<<", "<<
getSampleTypeName(type)<<")" <<std::endl;
return AL_NONE; return AL_NONE;
} }
@ -307,9 +304,11 @@ private:
friend class OpenAL_Output; friend class OpenAL_Output;
public: public:
OpenAL_SoundStream(ALuint src, DecoderPtr decoder, bool getLoudnessData=false); OpenAL_SoundStream(ALuint src, DecoderPtr decoder);
~OpenAL_SoundStream(); ~OpenAL_SoundStream();
bool init(bool getLoudnessData=false);
bool isPlaying(); bool isPlaying();
double getStreamDelay() const; double getStreamDelay() const;
double getStreamOffset() const; double getStreamOffset() const;
@ -394,20 +393,35 @@ private:
}; };
OpenAL_SoundStream::OpenAL_SoundStream(ALuint src, DecoderPtr decoder, bool getLoudnessData) OpenAL_SoundStream::OpenAL_SoundStream(ALuint src, DecoderPtr decoder)
: mSource(src), mCurrentBufIdx(0), mFrameSize(0), mSilence(0), mDecoder(decoder), mIsFinished(false) : mSource(src), mBuffers{0}, mCurrentBufIdx(0), mFormat(AL_NONE), mSampleRate(0)
, mBufferSize(0), mFrameSize(0), mSilence(0), mDecoder(std::move(decoder))
, mLoudnessAnalyzer(nullptr)
{
}
OpenAL_SoundStream::~OpenAL_SoundStream()
{
if(mBuffers[0] && alIsBuffer(mBuffers[0]))
alDeleteBuffers(sNumBuffers, mBuffers);
alGetError();
mDecoder->close();
}
bool OpenAL_SoundStream::init(bool getLoudnessData)
{ {
alGenBuffers(sNumBuffers, mBuffers); alGenBuffers(sNumBuffers, mBuffers);
throwALerror(); ALenum err = getALError();
try if(err != AL_NO_ERROR)
{ return false;
int srate;
ChannelConfig chans; ChannelConfig chans;
SampleType type; SampleType type;
mDecoder->getInfo(&srate, &chans, &type); mDecoder->getInfo(&mSampleRate, &chans, &type);
mFormat = getALFormat(chans, type); mFormat = getALFormat(chans, type);
mSampleRate = srate; if(!mFormat) return false;
switch(type) switch(type)
{ {
@ -417,26 +431,14 @@ OpenAL_SoundStream::OpenAL_SoundStream(ALuint src, DecoderPtr decoder, bool getL
} }
mFrameSize = framesToBytes(1, chans, type); mFrameSize = framesToBytes(1, chans, type);
mBufferSize = static_cast<ALuint>(sBufferLength*srate); mBufferSize = static_cast<ALuint>(sBufferLength*mSampleRate);
mBufferSize *= mFrameSize; mBufferSize *= mFrameSize;
if (getLoudnessData) if (getLoudnessData)
mLoudnessAnalyzer.reset(new Sound_Loudness(sLoudnessFPS, mSampleRate, chans, type)); mLoudnessAnalyzer.reset(new Sound_Loudness(sLoudnessFPS, mSampleRate, chans, type));
}
catch(std::exception&)
{
alDeleteBuffers(sNumBuffers, mBuffers);
alGetError();
throw;
}
mIsFinished = false;
}
OpenAL_SoundStream::~OpenAL_SoundStream()
{
alDeleteBuffers(sNumBuffers, mBuffers);
alGetError();
mDecoder->close(); mIsFinished = false;
return true;
} }
bool OpenAL_SoundStream::isPlaying() bool OpenAL_SoundStream::isPlaying()
@ -444,7 +446,7 @@ bool OpenAL_SoundStream::isPlaying()
ALint state; ALint state;
alGetSourcei(mSource, AL_SOURCE_STATE, &state); alGetSourcei(mSource, AL_SOURCE_STATE, &state);
throwALerror(); getALError();
if(state == AL_PLAYING || state == AL_PAUSED) if(state == AL_PLAYING || state == AL_PAUSED)
return true; return true;
@ -467,7 +469,7 @@ double OpenAL_SoundStream::getStreamDelay() const
d = (double)inqueue / (double)mSampleRate; d = (double)inqueue / (double)mSampleRate;
} }
throwALerror(); getALError();
return d; return d;
} }
@ -493,7 +495,7 @@ double OpenAL_SoundStream::getStreamOffset() const
t = (double)mDecoder->getSampleOffset() / (double)mSampleRate; t = (double)mDecoder->getSampleOffset() / (double)mSampleRate;
} }
throwALerror(); getALError();
return t; return t;
} }
@ -590,7 +592,7 @@ std::vector<std::string> OpenAL_Output::enumerate()
return devlist; return devlist;
} }
void OpenAL_Output::init(const std::string &devname) bool OpenAL_Output::init(const std::string &devname)
{ {
deinit(); deinit();
@ -598,9 +600,10 @@ void OpenAL_Output::init(const std::string &devname)
if(!mDevice) if(!mDevice)
{ {
if(devname.empty()) if(devname.empty())
fail("Failed to open default device"); std::cerr<< "Failed to open default audio device" <<std::endl;
else else
fail("Failed to open \""+devname+"\""); std::cerr<< "Failed to open \""<<devname<<"\"" <<std::endl;
return false;
} }
else else
{ {
@ -615,42 +618,49 @@ void OpenAL_Output::init(const std::string &devname)
mContext = alcCreateContext(mDevice, NULL); mContext = alcCreateContext(mDevice, NULL);
if(!mContext || alcMakeContextCurrent(mContext) == ALC_FALSE) if(!mContext || alcMakeContextCurrent(mContext) == ALC_FALSE)
{ {
std::cerr<< "Failed to setup audio context: "<<alcGetString(mDevice, alcGetError(mDevice)) <<std::endl;
if(mContext) if(mContext)
alcDestroyContext(mContext); alcDestroyContext(mContext);
mContext = 0; mContext = nullptr;
fail(std::string("Failed to setup context: ")+alcGetString(mDevice, alcGetError(mDevice))); alcCloseDevice(mDevice);
mDevice = nullptr;
return false;
} }
ALC.EXT_EFX = !!alcIsExtensionPresent(mDevice, "ALC_EXT_EFX"); ALC.EXT_EFX = !!alcIsExtensionPresent(mDevice, "ALC_EXT_EFX");
AL.SOFT_source_spatialize = !!alIsExtensionPresent("AL_SOFT_source_spatialize"); AL.SOFT_source_spatialize = !!alIsExtensionPresent("AL_SOFT_source_spatialize");
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); ALCuint maxtotal;
throwALerror(); ALCint maxmono = 0, maxstereo = 0;
ALCint maxmono=0, maxstereo=0;
alcGetIntegerv(mDevice, ALC_MONO_SOURCES, 1, &maxmono); alcGetIntegerv(mDevice, ALC_MONO_SOURCES, 1, &maxmono);
alcGetIntegerv(mDevice, ALC_STEREO_SOURCES, 1, &maxstereo); alcGetIntegerv(mDevice, ALC_STEREO_SOURCES, 1, &maxstereo);
throwALCerror(mDevice); if(getALCError(mDevice) != ALC_NO_ERROR)
maxtotal = 256;
try else
{ {
ALCuint maxtotal = std::min<ALCuint>(maxmono+maxstereo, 256); maxtotal = std::min<ALCuint>(maxmono+maxstereo, 256);
if (maxtotal == 0) // workaround for broken implementations if (maxtotal == 0) // workaround for broken implementations
maxtotal = 256; maxtotal = 256;
}
for(size_t i = 0;i < maxtotal;i++) for(size_t i = 0;i < maxtotal;i++)
{ {
ALuint src = 0; ALuint src = 0;
alGenSources(1, &src); alGenSources(1, &src);
throwALerror(); if(getALError() != AL_NO_ERROR)
break;
mFreeSources.push_back(src); mFreeSources.push_back(src);
} }
}
catch(std::exception &e)
{
std::cout <<"Error: "<<e.what()<<", trying to continue"<< std::endl;
}
if(mFreeSources.empty()) if(mFreeSources.empty())
fail("Could not allocate any sources"); {
std::cerr<< "Could not allocate any sound sources" <<std::endl;
alcMakeContextCurrent(nullptr);
alcDestroyContext(mContext);
mContext = nullptr;
alcCloseDevice(mDevice);
mDevice = nullptr;
return false;
}
std::cout<< "Allocated "<<mFreeSources.size()<<" sound sources" <<std::endl;
if(ALC.EXT_EFX) if(ALC.EXT_EFX)
{ {
@ -689,7 +699,11 @@ void OpenAL_Output::init(const std::string &devname)
LOAD_FUNC(alGetAuxiliaryEffectSlotf); LOAD_FUNC(alGetAuxiliaryEffectSlotf);
LOAD_FUNC(alGetAuxiliaryEffectSlotfv); LOAD_FUNC(alGetAuxiliaryEffectSlotfv);
#undef LOAD_FUNC #undef LOAD_FUNC
throwALerror(); if(getALError() != AL_NO_ERROR)
{
ALC.EXT_EFX = false;
goto skip_efx;
}
alGenFilters(1, &mWaterFilter); alGenFilters(1, &mWaterFilter);
if(alGetError() == AL_NO_ERROR) if(alGetError() == AL_NO_ERROR)
@ -743,12 +757,15 @@ void OpenAL_Output::init(const std::string &devname)
alListenerf(AL_METERS_PER_UNIT, 1.0f / UnitsPerMeter); alListenerf(AL_METERS_PER_UNIT, 1.0f / UnitsPerMeter);
} }
skip_efx:
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
// Speed of sound is in units per second. Given the default speed of sound is 343.3 (assumed // Speed of sound is in units per second. Given the default speed of sound is 343.3 (assumed
// meters per second), multiply by the units per meter to get the speed in u/s. // meters per second), multiply by the units per meter to get the speed in u/s.
alSpeedOfSound(343.3f * UnitsPerMeter); alSpeedOfSound(343.3f * UnitsPerMeter);
alGetError(); alGetError();
mInitialized = true; mInitialized = true;
return true;
} }
void OpenAL_Output::deinit() void OpenAL_Output::deinit()
@ -786,11 +803,9 @@ void OpenAL_Output::deinit()
std::vector<std::string> OpenAL_Output::enumerateHrtf() std::vector<std::string> OpenAL_Output::enumerateHrtf()
{ {
if(!mDevice)
fail("Device not initialized");
std::vector<std::string> ret; std::vector<std::string> ret;
if(!alcIsExtensionPresent(mDevice, "ALC_SOFT_HRTF"))
if(!mDevice || !alcIsExtensionPresent(mDevice, "ALC_SOFT_HRTF"))
return ret; return ret;
LPALCGETSTRINGISOFT alcGetStringiSOFT = 0; LPALCGETSTRINGISOFT alcGetStringiSOFT = 0;
@ -816,7 +831,6 @@ void OpenAL_Output::enableHrtf(const std::string &hrtfname, bool auto_enable)
return; return;
} }
LPALCGETSTRINGISOFT alcGetStringiSOFT = 0; LPALCGETSTRINGISOFT alcGetStringiSOFT = 0;
getALCFunc(alcGetStringiSOFT, mDevice, "alcGetStringiSOFT"); getALCFunc(alcGetStringiSOFT, mDevice, "alcGetStringiSOFT");
@ -891,7 +905,7 @@ void OpenAL_Output::disableHrtf()
Sound_Handle OpenAL_Output::loadSound(const std::string &fname) Sound_Handle OpenAL_Output::loadSound(const std::string &fname)
{ {
throwALerror(); getALError();
DecoderPtr decoder = mManager.getDecoder(); DecoderPtr decoder = mManager.getDecoder();
// Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav. // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
@ -914,20 +928,20 @@ Sound_Handle OpenAL_Output::loadSound(const std::string &fname)
decoder->getInfo(&srate, &chans, &type); decoder->getInfo(&srate, &chans, &type);
format = getALFormat(chans, type); format = getALFormat(chans, type);
if(!format) return nullptr;
decoder->readAll(data); decoder->readAll(data);
decoder->close(); decoder->close();
ALuint buf = 0; ALuint buf = 0;
try {
alGenBuffers(1, &buf); alGenBuffers(1, &buf);
alBufferData(buf, format, &data[0], data.size(), srate); alBufferData(buf, format, &data[0], data.size(), srate);
throwALerror(); if(getALError() != AL_NO_ERROR)
} {
catch(...) {
if(buf && alIsBuffer(buf)) if(buf && alIsBuffer(buf))
alDeleteBuffers(1, &buf); alDeleteBuffers(1, &buf);
throw; getALError();
return nullptr;
} }
return MAKE_PTRID(buf); return MAKE_PTRID(buf);
} }
@ -952,6 +966,7 @@ void OpenAL_Output::unloadSound(Sound_Handle data)
} }
} }
alDeleteBuffers(1, &buffer); alDeleteBuffers(1, &buffer);
getALError();
} }
size_t OpenAL_Output::getSoundDataSize(Sound_Handle data) const size_t OpenAL_Output::getSoundDataSize(Sound_Handle data) const
@ -960,7 +975,7 @@ size_t OpenAL_Output::getSoundDataSize(Sound_Handle data) const
ALint size = 0; ALint size = 0;
alGetBufferi(buffer, AL_SIZE, &size); alGetBufferi(buffer, AL_SIZE, &size);
throwALerror(); getALError();
return (ALuint)size; return (ALuint)size;
} }
@ -1067,65 +1082,63 @@ void OpenAL_Output::updateCommon(ALuint source, const osg::Vec3f& pos, ALfloat m
} }
void OpenAL_Output::playSound(Sound *sound, Sound_Handle data, float offset) bool OpenAL_Output::playSound(Sound *sound, Sound_Handle data, float offset)
{ {
ALuint source; ALuint source;
if(mFreeSources.empty()) if(mFreeSources.empty())
fail("No free sources"); {
std::cerr<< "No free sources!" <<std::endl;
return false;
}
source = mFreeSources.front(); source = mFreeSources.front();
mFreeSources.pop_front();
try {
initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(),
sound->getIsLooping(), sound->getUseEnv()); sound->getIsLooping(), sound->getUseEnv());
alSourcef(source, AL_SEC_OFFSET, offset); alSourcef(source, AL_SEC_OFFSET, offset);
throwALerror(); if(getALError() != AL_NO_ERROR)
return false;
alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcei(source, AL_BUFFER, GET_PTRID(data));
alSourcePlay(source); alSourcePlay(source);
throwALerror(); if(getALError() != AL_NO_ERROR)
return false;
mActiveSounds.push_back(sound);
}
catch(std::exception&) {
mFreeSources.push_back(source);
throw;
}
mFreeSources.pop_front();
sound->mHandle = MAKE_PTRID(source); sound->mHandle = MAKE_PTRID(source);
mActiveSounds.push_back(sound);
return true;
} }
void OpenAL_Output::playSound3D(Sound *sound, Sound_Handle data, float offset) bool OpenAL_Output::playSound3D(Sound *sound, Sound_Handle data, float offset)
{ {
ALuint source; ALuint source;
if(mFreeSources.empty()) if(mFreeSources.empty())
fail("No free sources"); {
std::cerr<< "No free sources!" <<std::endl;
return false;
}
source = mFreeSources.front(); source = mFreeSources.front();
mFreeSources.pop_front();
try {
initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(),
sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(), sound->getRealVolume(), sound->getPitch(), sound->getIsLooping(),
sound->getUseEnv()); sound->getUseEnv());
alSourcef(source, AL_SEC_OFFSET, offset); alSourcef(source, AL_SEC_OFFSET, offset);
throwALerror(); if(getALError() != AL_NO_ERROR)
return false;
alSourcei(source, AL_BUFFER, GET_PTRID(data)); alSourcei(source, AL_BUFFER, GET_PTRID(data));
alSourcePlay(source); alSourcePlay(source);
throwALerror(); if(getALError() != AL_NO_ERROR)
return false;
mActiveSounds.push_back(sound);
}
catch(std::exception&) {
mFreeSources.push_back(source);
throw;
}
mFreeSources.pop_front();
sound->mHandle = MAKE_PTRID(source); sound->mHandle = MAKE_PTRID(source);
mActiveSounds.push_back(sound);
return true;
} }
void OpenAL_Output::finishSound(Sound *sound) void OpenAL_Output::finishSound(Sound *sound)
@ -1139,6 +1152,7 @@ void OpenAL_Output::finishSound(Sound *sound)
// the initial queue already played when it hasn't. // the initial queue already played when it hasn't.
alSourceRewind(source); alSourceRewind(source);
alSourcei(source, AL_BUFFER, 0); alSourcei(source, AL_BUFFER, 0);
getALError();
mFreeSources.push_back(source); mFreeSources.push_back(source);
mActiveSounds.erase(std::find(mActiveSounds.begin(), mActiveSounds.end(), sound)); mActiveSounds.erase(std::find(mActiveSounds.begin(), mActiveSounds.end(), sound));
@ -1148,10 +1162,10 @@ bool OpenAL_Output::isSoundPlaying(Sound *sound)
{ {
if(!sound->mHandle) return false; if(!sound->mHandle) return false;
ALuint source = GET_PTRID(sound->mHandle); ALuint source = GET_PTRID(sound->mHandle);
ALint state; ALint state = AL_STOPPED;
alGetSourcei(source, AL_SOURCE_STATE, &state); alGetSourcei(source, AL_SOURCE_STATE, &state);
throwALerror(); getALError();
return state == AL_PLAYING || state == AL_PAUSED; return state == AL_PLAYING || state == AL_PAUSED;
} }
@ -1163,69 +1177,68 @@ void OpenAL_Output::updateSound(Sound *sound)
updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(),
sound->getPitch(), sound->getUseEnv(), sound->getIs3D()); sound->getPitch(), sound->getUseEnv(), sound->getIs3D());
getALError();
} }
void OpenAL_Output::streamSound(DecoderPtr decoder, Stream *sound) bool OpenAL_Output::streamSound(DecoderPtr decoder, Stream *sound)
{ {
OpenAL_SoundStream *stream = 0;
ALuint source;
if(mFreeSources.empty()) if(mFreeSources.empty())
fail("No free sources"); {
source = mFreeSources.front(); std::cerr<< "No free sources!" <<std::endl;
mFreeSources.pop_front(); return false;
}
ALuint source = mFreeSources.front();
if(sound->getIsLooping()) if(sound->getIsLooping())
std::cout <<"Warning: cannot loop stream \""<<decoder->getName()<<"\""<< std::endl; std::cout <<"Warning: cannot loop stream \""<<decoder->getName()<<"\""<< std::endl;
try {
initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(), initCommon2D(source, sound->getPosition(), sound->getRealVolume(), sound->getPitch(),
false, sound->getUseEnv()); false, sound->getUseEnv());
throwALerror(); if(getALError() != AL_NO_ERROR)
return false;
stream = new OpenAL_SoundStream(source, decoder); OpenAL_SoundStream *stream = new OpenAL_SoundStream(source, std::move(decoder));
mStreamThread->add(stream); if(!stream->init())
mActiveStreams.push_back(sound); {
}
catch(std::exception&) {
mStreamThread->remove(stream);
delete stream; delete stream;
mFreeSources.push_back(source); return false;
throw;
} }
mStreamThread->add(stream);
mFreeSources.pop_front();
sound->mHandle = stream; sound->mHandle = stream;
mActiveStreams.push_back(sound);
return true;
} }
void OpenAL_Output::streamSound3D(DecoderPtr decoder, Stream *sound, bool getLoudnessData) bool OpenAL_Output::streamSound3D(DecoderPtr decoder, Stream *sound, bool getLoudnessData)
{ {
OpenAL_SoundStream *stream = 0;
ALuint source;
if(mFreeSources.empty()) if(mFreeSources.empty())
fail("No free sources"); {
source = mFreeSources.front(); std::cerr<< "No free sources!" <<std::endl;
mFreeSources.pop_front(); return false;
}
ALuint source = mFreeSources.front();
if(sound->getIsLooping()) if(sound->getIsLooping())
std::cout <<"Warning: cannot loop stream \""<<decoder->getName()<<"\""<< std::endl; std::cout <<"Warning: cannot loop stream \""<<decoder->getName()<<"\""<< std::endl;
try {
initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(), initCommon3D(source, sound->getPosition(), sound->getMinDistance(), sound->getMaxDistance(),
sound->getRealVolume(), sound->getPitch(), false, sound->getUseEnv()); sound->getRealVolume(), sound->getPitch(), false, sound->getUseEnv());
throwALerror(); if(getALError() != AL_NO_ERROR)
return false;
stream = new OpenAL_SoundStream(source, decoder, getLoudnessData); OpenAL_SoundStream *stream = new OpenAL_SoundStream(source, std::move(decoder));
mStreamThread->add(stream); if(!stream->init(getLoudnessData))
mActiveStreams.push_back(sound); {
}
catch(std::exception&) {
mStreamThread->remove(stream);
delete stream; delete stream;
mFreeSources.push_back(source); return false;
throw;
} }
mStreamThread->add(stream);
mFreeSources.pop_front();
sound->mHandle = stream; sound->mHandle = stream;
mActiveStreams.push_back(sound);
return true;
} }
void OpenAL_Output::finishStream(Stream *sound) void OpenAL_Output::finishStream(Stream *sound)
@ -1242,6 +1255,7 @@ void OpenAL_Output::finishStream(Stream *sound)
// the initial queue already played when it hasn't. // the initial queue already played when it hasn't.
alSourceRewind(source); alSourceRewind(source);
alSourcei(source, AL_BUFFER, 0); alSourcei(source, AL_BUFFER, 0);
getALError();
mFreeSources.push_back(source); mFreeSources.push_back(source);
mActiveStreams.erase(std::find(mActiveStreams.begin(), mActiveStreams.end(), sound)); mActiveStreams.erase(std::find(mActiveStreams.begin(), mActiveStreams.end(), sound));
@ -1288,6 +1302,7 @@ void OpenAL_Output::updateStream(Stream *sound)
updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(), updateCommon(source, sound->getPosition(), sound->getMaxDistance(), sound->getRealVolume(),
sound->getPitch(), sound->getUseEnv(), sound->getIs3D()); sound->getPitch(), sound->getUseEnv(), sound->getIs3D());
getALError();
} }
@ -1346,7 +1361,7 @@ void OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdi
(env == Env_Underwater) ? mWaterEffect : mDefaultEffect (env == Env_Underwater) ? mWaterEffect : mDefaultEffect
); );
} }
throwALerror(); getALError();
} }
mListenerPos = pos; mListenerPos = pos;
@ -1374,8 +1389,8 @@ void OpenAL_Output::pauseSounds(int types)
} }
if(!sources.empty()) if(!sources.empty())
{ {
alSourcePausev(sources.size(), &sources[0]); alSourcePausev(sources.size(), sources.data());
throwALerror(); getALError();
} }
} }
@ -1399,8 +1414,8 @@ void OpenAL_Output::resumeSounds(int types)
} }
if(!sources.empty()) if(!sources.empty())
{ {
alSourcePlayv(sources.size(), &sources[0]); alSourcePlayv(sources.size(), sources.data());
throwALerror(); getALError();
} }
} }

View file

@ -59,7 +59,7 @@ namespace MWSound
public: public:
virtual std::vector<std::string> enumerate(); virtual std::vector<std::string> enumerate();
virtual void init(const std::string &devname=std::string()); virtual bool init(const std::string &devname=std::string());
virtual void deinit(); virtual void deinit();
virtual std::vector<std::string> enumerateHrtf(); virtual std::vector<std::string> enumerateHrtf();
@ -70,14 +70,14 @@ namespace MWSound
virtual void unloadSound(Sound_Handle data); virtual void unloadSound(Sound_Handle data);
virtual size_t getSoundDataSize(Sound_Handle data) const; virtual size_t getSoundDataSize(Sound_Handle data) const;
virtual void playSound(Sound *sound, Sound_Handle data, float offset); virtual bool playSound(Sound *sound, Sound_Handle data, float offset);
virtual void playSound3D(Sound *sound, Sound_Handle data, float offset); virtual bool playSound3D(Sound *sound, Sound_Handle data, float offset);
virtual void finishSound(Sound *sound); virtual void finishSound(Sound *sound);
virtual bool isSoundPlaying(Sound *sound); virtual bool isSoundPlaying(Sound *sound);
virtual void updateSound(Sound *sound); virtual void updateSound(Sound *sound);
virtual void streamSound(DecoderPtr decoder, Stream *sound); virtual bool streamSound(DecoderPtr decoder, Stream *sound);
virtual void streamSound3D(DecoderPtr decoder, Stream *sound, bool getLoudnessData); virtual bool streamSound3D(DecoderPtr decoder, Stream *sound, bool getLoudnessData);
virtual void finishStream(Stream *sound); virtual void finishStream(Stream *sound);
virtual double getStreamDelay(Stream *sound); virtual double getStreamDelay(Stream *sound);
virtual double getStreamOffset(Stream *sound); virtual double getStreamOffset(Stream *sound);

View file

@ -24,7 +24,7 @@ namespace MWSound
SoundManager &mManager; SoundManager &mManager;
virtual std::vector<std::string> enumerate() = 0; virtual std::vector<std::string> enumerate() = 0;
virtual void init(const std::string &devname=std::string()) = 0; virtual bool init(const std::string &devname=std::string()) = 0;
virtual void deinit() = 0; virtual void deinit() = 0;
virtual std::vector<std::string> enumerateHrtf() = 0; virtual std::vector<std::string> enumerateHrtf() = 0;
@ -35,14 +35,14 @@ namespace MWSound
virtual void unloadSound(Sound_Handle data) = 0; virtual void unloadSound(Sound_Handle data) = 0;
virtual size_t getSoundDataSize(Sound_Handle data) const = 0; virtual size_t getSoundDataSize(Sound_Handle data) const = 0;
virtual void playSound(Sound *sound, Sound_Handle data, float offset) = 0; virtual bool playSound(Sound *sound, Sound_Handle data, float offset) = 0;
virtual void playSound3D(Sound *sound, Sound_Handle data, float offset) = 0; virtual bool playSound3D(Sound *sound, Sound_Handle data, float offset) = 0;
virtual void finishSound(Sound *sound) = 0; virtual void finishSound(Sound *sound) = 0;
virtual bool isSoundPlaying(Sound *sound) = 0; virtual bool isSoundPlaying(Sound *sound) = 0;
virtual void updateSound(Sound *sound) = 0; virtual void updateSound(Sound *sound) = 0;
virtual void streamSound(DecoderPtr decoder, Stream *sound) = 0; virtual bool streamSound(DecoderPtr decoder, Stream *sound) = 0;
virtual void streamSound3D(DecoderPtr decoder, Stream *sound, bool getLoudnessData) = 0; virtual bool streamSound3D(DecoderPtr decoder, Stream *sound, bool getLoudnessData) = 0;
virtual void finishStream(Stream *sound) = 0; virtual void finishStream(Stream *sound) = 0;
virtual double getStreamDelay(Stream *sound) = 0; virtual double getStreamDelay(Stream *sound) = 0;
virtual double getStreamOffset(Stream *sound) = 0; virtual double getStreamOffset(Stream *sound) = 0;

View file

@ -89,30 +89,36 @@ namespace MWSound
std::cout << "Sound output: " << SOUND_OUT << std::endl; std::cout << "Sound output: " << SOUND_OUT << std::endl;
std::cout << "Sound decoder: " << SOUND_IN << std::endl; std::cout << "Sound decoder: " << SOUND_IN << std::endl;
try {
std::vector<std::string> names = mOutput->enumerate(); std::vector<std::string> names = mOutput->enumerate();
std::cout <<"Enumerated output devices:"<< std::endl; std::cout <<"Enumerated output devices:\n";
for(size_t i = 0;i < names.size();i++) std::for_each(names.cbegin(), names.cend(),
std::cout <<" "<<names[i]<< std::endl; [](const std::string &name) -> void
{ std::cout <<" "<<name<<"\n"; }
);
std::cout.flush();
std::string devname = Settings::Manager::getString("device", "Sound"); std::string devname = Settings::Manager::getString("device", "Sound");
try { bool inited = mOutput->init(devname);
mOutput->init(devname); if(!inited && !devname.empty())
{
std::cerr<< "Failed to initialize device \""<<devname<<"\", trying default" <<std::endl;
inited = mOutput->init();
} }
catch(std::exception &e) { if(!inited)
if(devname.empty()) {
throw; std::cerr<< "Failed to initialize default audio device, sound disabled" <<std::endl;
std::cerr <<"Failed to open device \""<<devname<<"\": " << e.what() << std::endl; return;
mOutput->init();
Settings::Manager::setString("device", "Sound", "");
} }
names = mOutput->enumerateHrtf(); names = mOutput->enumerateHrtf();
if(!names.empty()) if(!names.empty())
{ {
std::cout <<"Enumerated HRTF names:"<< std::endl; std::cout<< "Enumerated HRTF names:\n";
for(size_t i = 0;i < names.size();i++) std::for_each(names.cbegin(), names.cend(),
std::cout <<" "<<names[i]<< std::endl; [](const std::string &name) -> void
{ std::cout<< " "<<name<<"\n"; }
);
std::cout.flush();
} }
if(hrtfstate == 0) if(hrtfstate == 0)
@ -120,10 +126,6 @@ namespace MWSound
else if(!hrtfname.empty()) else if(!hrtfname.empty())
mOutput->enableHrtf(hrtfname, hrtfstate<0); mOutput->enableHrtf(hrtfname, hrtfstate<0);
} }
catch(std::exception &e) {
std::cout <<"Sound init failed: "<<e.what()<< std::endl;
}
}
SoundManager::~SoundManager() SoundManager::~SoundManager()
{ {
@ -186,8 +188,12 @@ namespace MWSound
Sound_Buffer *SoundManager::lookupSound(const std::string &soundId) const Sound_Buffer *SoundManager::lookupSound(const std::string &soundId) const
{ {
NameBufferMap::const_iterator snd = mBufferNameMap.find(soundId); NameBufferMap::const_iterator snd = mBufferNameMap.find(soundId);
if(snd != mBufferNameMap.end()) return snd->second; if(snd != mBufferNameMap.end())
return 0; {
Sound_Buffer *sfx = snd->second;
if(sfx->mHandle) return sfx;
}
return nullptr;
} }
// Lookup a soundId for its sound data (resource name, local volume, // Lookup a soundId for its sound data (resource name, local volume,
@ -201,15 +207,17 @@ namespace MWSound
else else
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
const ESM::Sound *sound = world->getStore().get<ESM::Sound>().find(soundId); const ESM::Sound *sound = world->getStore().get<ESM::Sound>().search(soundId);
if(!sound) return nullptr;
sfx = insertSound(soundId, sound); sfx = insertSound(soundId, sound);
} }
if(!sfx->mHandle) if(!sfx->mHandle)
{ {
sfx->mHandle = mOutput->loadSound(sfx->mResourceName); sfx->mHandle = mOutput->loadSound(sfx->mResourceName);
mBufferCacheSize += mOutput->getSoundDataSize(sfx->mHandle); if(!sfx->mHandle) return nullptr;
mBufferCacheSize += mOutput->getSoundDataSize(sfx->mHandle);
if(mBufferCacheSize > mBufferCacheMax) if(mBufferCacheSize > mBufferCacheMax)
{ {
do { do {
@ -293,18 +301,24 @@ namespace MWSound
static float minDistance = std::max(fAudioVoiceDefaultMinDistance * fAudioMinDistanceMult, 1.0f); static float minDistance = std::max(fAudioVoiceDefaultMinDistance * fAudioMinDistanceMult, 1.0f);
static float maxDistance = std::max(fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult, minDistance); static float maxDistance = std::max(fAudioVoiceDefaultMaxDistance * fAudioMaxDistanceMult, minDistance);
bool played;
float basevol = volumeFromType(Play_TypeVoice); float basevol = volumeFromType(Play_TypeVoice);
Stream *sound = getStreamRef(); Stream *sound = getStreamRef();
if(playlocal) if(playlocal)
{ {
sound->init(1.0f, basevol, 1.0f, Play_NoEnv|Play_TypeVoice|Play_2D); sound->init(1.0f, basevol, 1.0f, Play_NoEnv|Play_TypeVoice|Play_2D);
mOutput->streamSound(decoder, sound); played = mOutput->streamSound(decoder, sound);
} }
else else
{ {
sound->init(pos, 1.0f, basevol, 1.0f, minDistance, maxDistance, sound->init(pos, 1.0f, basevol, 1.0f, minDistance, maxDistance,
Play_Normal|Play_TypeVoice|Play_3D); Play_Normal|Play_TypeVoice|Play_3D);
mOutput->streamSound3D(decoder, sound, true); played = mOutput->streamSound3D(decoder, sound, true);
}
if(!played)
{
mUnusedStreams.push_back(sound);
return nullptr;
} }
return sound; return sound;
} }
@ -351,7 +365,7 @@ namespace MWSound
return; return;
std::cout <<"Playing "<<filename<< std::endl; std::cout <<"Playing "<<filename<< std::endl;
mLastPlayedMusic = filename; mLastPlayedMusic = filename;
try {
stopMusic(); stopMusic();
DecoderPtr decoder = getDecoder(); DecoderPtr decoder = getDecoder();
@ -362,13 +376,6 @@ namespace MWSound
Play_NoEnv|Play_TypeMusic|Play_2D); Play_NoEnv|Play_TypeMusic|Play_2D);
mOutput->streamSound(decoder, mMusic); mOutput->streamSound(decoder, mMusic);
} }
catch(std::exception &e) {
std::cout << "Music Error: " << e.what() << "\n";
if(mMusic)
mUnusedStreams.push_back(mMusic);
mMusic = nullptr;
}
}
void SoundManager::advanceMusic(const std::string& filename) void SoundManager::advanceMusic(const std::string& filename)
{ {
@ -454,8 +461,7 @@ namespace MWSound
{ {
if(!mOutput->isInitialized()) if(!mOutput->isInitialized())
return; return;
try
{
std::string voicefile = "Sound/"+filename; std::string voicefile = "Sound/"+filename;
mVFS->normalizeFilename(voicefile); mVFS->normalizeFilename(voicefile);
@ -466,13 +472,10 @@ namespace MWSound
stopSay(ptr); stopSay(ptr);
Stream *sound = playVoice(decoder, pos, (ptr == MWMechanics::getPlayer())); Stream *sound = playVoice(decoder, pos, (ptr == MWMechanics::getPlayer()));
if(!sound) return;
mActiveSaySounds.insert(std::make_pair(ptr, sound)); mActiveSaySounds.insert(std::make_pair(ptr, sound));
} }
catch(std::exception &e)
{
std::cout <<"Sound Error: "<<e.what()<< std::endl;
}
}
float SoundManager::getSaySoundLoudness(const MWWorld::ConstPtr &ptr) const float SoundManager::getSaySoundLoudness(const MWWorld::ConstPtr &ptr) const
{ {
@ -490,21 +493,17 @@ namespace MWSound
{ {
if(!mOutput->isInitialized()) if(!mOutput->isInitialized())
return; return;
try
{
std::string voicefile = "Sound/"+filename; std::string voicefile = "Sound/"+filename;
mVFS->normalizeFilename(voicefile); mVFS->normalizeFilename(voicefile);
DecoderPtr decoder = loadVoice(voicefile); DecoderPtr decoder = loadVoice(voicefile);
stopSay(MWWorld::ConstPtr()); stopSay(MWWorld::ConstPtr());
mActiveSaySounds.insert(std::make_pair(MWWorld::ConstPtr(), Stream *sound = playVoice(decoder, osg::Vec3f(), true);
playVoice(decoder, osg::Vec3f(), true))); if(!sound) return;
}
catch(std::exception &e) mActiveSaySounds.insert(std::make_pair(MWWorld::ConstPtr(), sound));
{
std::cout <<"Sound Error: "<<e.what()<< std::endl;
}
} }
bool SoundManager::sayDone(const MWWorld::ConstPtr &ptr) const bool SoundManager::sayDone(const MWWorld::ConstPtr &ptr) const
@ -535,22 +534,18 @@ namespace MWSound
{ {
if(!mOutput->isInitialized()) if(!mOutput->isInitialized())
return nullptr; return nullptr;
Stream *track = getStreamRef();
try
{
track->init(1.0f, volumeFromType(type), 1.0f, Play_NoEnv|type|Play_2D);
mOutput->streamSound(decoder, track);
TrackList::iterator iter = std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), track); Stream *track = getStreamRef();
mActiveTracks.insert(iter, track); track->init(1.0f, volumeFromType(type), 1.0f, Play_NoEnv|type|Play_2D);
} if(!mOutput->streamSound(decoder, track))
catch(std::exception &e)
{ {
std::cout <<"Sound Error: "<<e.what()<< std::endl;
if(track)
mUnusedStreams.push_back(track); mUnusedStreams.push_back(track);
track = nullptr; return nullptr;
} }
mActiveTracks.insert(
std::lower_bound(mActiveTracks.begin(), mActiveTracks.end(), track), track
);
return track; return track;
} }
@ -573,15 +568,18 @@ namespace MWSound
{ {
if(!mOutput->isInitialized()) if(!mOutput->isInitialized())
return nullptr; return nullptr;
Sound *sound = nullptr;
try
{
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
float basevol = volumeFromType(type);
sound = getSoundRef(); Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
sound->init(volume * sfx->mVolume, basevol, pitch, mode|type|Play_2D); if(!sfx) return nullptr;
mOutput->playSound(sound, sfx->mHandle, offset);
Sound *sound = getSoundRef();
sound->init(volume * sfx->mVolume, volumeFromType(type), pitch, mode|type|Play_2D);
if(!mOutput->playSound(sound, sfx->mHandle, offset))
{
mUnusedSounds.push_back(sound);
return nullptr;
}
if(sfx->mUses++ == 0) if(sfx->mUses++ == 0)
{ {
SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx); SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx);
@ -589,14 +587,6 @@ namespace MWSound
mUnusedBuffers.erase(iter); mUnusedBuffers.erase(iter);
} }
mActiveSounds[MWWorld::ConstPtr()].push_back(std::make_pair(sound, sfx)); mActiveSounds[MWWorld::ConstPtr()].push_back(std::make_pair(sound, sfx));
}
catch(std::exception&)
{
//std::cout <<"Sound Error: "<<e.what()<< std::endl;
if(sound)
mUnusedSounds.push_back(sound);
sound = nullptr;
}
return sound; return sound;
} }
@ -606,33 +596,37 @@ namespace MWSound
{ {
if(!mOutput->isInitialized()) if(!mOutput->isInitialized())
return nullptr; return nullptr;
Sound *sound = nullptr;
try
{
// Look up the sound in the ESM data // Look up the sound in the ESM data
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
float basevol = volumeFromType(type); if(!sfx) return nullptr;
const ESM::Position &pos = ptr.getRefData().getPosition();
const osg::Vec3f objpos(pos.asVec3());
const osg::Vec3f objpos(ptr.getRefData().getPosition().asVec3());
if((mode&Play_RemoveAtDistance) && (mListenerPos-objpos).length2() > 2000*2000) if((mode&Play_RemoveAtDistance) && (mListenerPos-objpos).length2() > 2000*2000)
return nullptr; return nullptr;
// Only one copy of given sound can be played at time on ptr, so stop previous copy // Only one copy of given sound can be played at time on ptr, so stop previous copy
stopSound3D(ptr, soundId); stopSound3D(ptr, soundId);
sound = getSoundRef(); bool played;
Sound *sound = getSoundRef();
if(!(mode&Play_NoPlayerLocal) && ptr == MWMechanics::getPlayer()) if(!(mode&Play_NoPlayerLocal) && ptr == MWMechanics::getPlayer())
{ {
sound->init(volume * sfx->mVolume, basevol, pitch, mode|type|Play_2D); sound->init(volume * sfx->mVolume, volumeFromType(type), pitch, mode|type|Play_2D);
mOutput->playSound(sound, sfx->mHandle, offset); played = mOutput->playSound(sound, sfx->mHandle, offset);
} }
else else
{ {
sound->init(objpos, volume * sfx->mVolume, basevol, pitch, sound->init(objpos, volume * sfx->mVolume, volumeFromType(type), pitch,
sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D); sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D);
mOutput->playSound3D(sound, sfx->mHandle, offset); played = mOutput->playSound3D(sound, sfx->mHandle, offset);
} }
if(!played)
{
mUnusedSounds.push_back(sound);
return nullptr;
}
if(sfx->mUses++ == 0) if(sfx->mUses++ == 0)
{ {
SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx); SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx);
@ -640,14 +634,6 @@ namespace MWSound
mUnusedBuffers.erase(iter); mUnusedBuffers.erase(iter);
} }
mActiveSounds[ptr].push_back(std::make_pair(sound, sfx)); mActiveSounds[ptr].push_back(std::make_pair(sound, sfx));
}
catch(std::exception&)
{
//std::cout <<"Sound Error: "<<e.what()<< std::endl;
if(sound)
mUnusedSounds.push_back(sound);
sound = nullptr;
}
return sound; return sound;
} }
@ -657,17 +643,20 @@ namespace MWSound
{ {
if(!mOutput->isInitialized()) if(!mOutput->isInitialized())
return nullptr; return nullptr;
Sound *sound = nullptr;
try
{
// Look up the sound in the ESM data // Look up the sound in the ESM data
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
float basevol = volumeFromType(type); if(!sfx) return nullptr;
sound = getSoundRef(); Sound *sound = getSoundRef();
sound->init(initialPos, volume * sfx->mVolume, basevol, pitch, sound->init(initialPos, volume * sfx->mVolume, volumeFromType(type), pitch,
sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D); sfx->mMinDist, sfx->mMaxDist, mode|type|Play_3D);
mOutput->playSound3D(sound, sfx->mHandle, offset); if(!mOutput->playSound3D(sound, sfx->mHandle, offset))
{
mUnusedSounds.push_back(sound);
return nullptr;
}
if(sfx->mUses++ == 0) if(sfx->mUses++ == 0)
{ {
SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx); SoundList::iterator iter = std::find(mUnusedBuffers.begin(), mUnusedBuffers.end(), sfx);
@ -675,14 +664,6 @@ namespace MWSound
mUnusedBuffers.erase(iter); mUnusedBuffers.erase(iter);
} }
mActiveSounds[MWWorld::ConstPtr()].push_back(std::make_pair(sound, sfx)); mActiveSounds[MWWorld::ConstPtr()].push_back(std::make_pair(sound, sfx));
}
catch(std::exception &)
{
//std::cout <<"Sound Error: "<<e.what()<< std::endl;
if(sound)
mUnusedSounds.push_back(sound);
sound = nullptr;
}
return sound; return sound;
} }