From 51a0f3903c812b36f482dbaf289042deb430796b Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Tue, 30 Sep 2014 09:13:12 +0200 Subject: [PATCH] Audio: Clean up threading and add AudioStream fadein (RGSS3) --- CMakeLists.txt | 1 + README.md | 1 - mkxp.pro | 3 +- src/alstream.cpp | 33 ++++------ src/alstream.h | 12 ++-- src/audio.cpp | 34 ++++------ src/audiostream.cpp | 149 +++++++++++++++++++++++++++++++------------- src/audiostream.h | 64 ++++++++++++------- src/eventthread.h | 29 +-------- src/sdl-util.h | 48 ++++++++++++++ 10 files changed, 228 insertions(+), 146 deletions(-) create mode 100644 src/sdl-util.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 386452d..85e58e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,6 +153,7 @@ set(MAIN_HEADERS src/tileatlasvx.h src/sharedmidistate.h src/fluid-fun.h + src/sdl-util.h ) set(MAIN_SOURCE diff --git a/README.md b/README.md index cb3af89..f4ba120 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ Missing RGSS3 functionality: * Text outline * Movie playback -* Audio fade-in Some other things might be implemented, but simply not bound yet. diff --git a/mkxp.pro b/mkxp.pro index 37c348c..0adb918 100644 --- a/mkxp.pro +++ b/mkxp.pro @@ -131,7 +131,8 @@ HEADERS += \ src/tilemapvx.h \ src/tileatlasvx.h \ src/sharedmidistate.h \ - src/fluid-fun.h + src/fluid-fun.h \ + src/sdl-util.h SOURCES += \ src/main.cpp \ diff --git a/src/alstream.cpp b/src/alstream.cpp index 6ab6358..da1e844 100644 --- a/src/alstream.cpp +++ b/src/alstream.cpp @@ -26,6 +26,7 @@ #include "filesystem.h" #include "aldatasource.h" #include "fluid-fun.h" +#include "sdl-util.h" #include #include @@ -38,8 +39,6 @@ ALStream::ALStream(LoopMode loopMode, source(0), thread(0), preemptPause(false), - streamInited(false), - needsRewind(false), pitch(1.0) { alSrc = AL::Source::gen(); @@ -198,7 +197,7 @@ void ALStream::openSource(const std::string &filename) { const char *ext; shState->fileSystem().openRead(srcOps, filename.c_str(), FileSystem::Audio, false, &ext); - needsRewind = false; + needsRewind.clear(); /* Try to read ogg file signature */ char sig[5] = { 0 }; @@ -227,13 +226,13 @@ void ALStream::openSource(const std::string &filename) void ALStream::stopStream() { - threadTermReq = true; + threadTermReq.set(); if (thread) { SDL_WaitThread(thread, 0); thread = 0; - needsRewind = true; + needsRewind.set(); } /* Need to stop the source _after_ the thread has terminated, @@ -249,14 +248,15 @@ void ALStream::startStream(float offset) AL::Source::clearQueue(alSrc); preemptPause = false; - streamInited = false; - sourceExhausted = false; - threadTermReq = false; + streamInited.clear(); + sourceExhausted.clear(); + threadTermReq.clear(); startOffset = offset; procFrames = offset * source->sampleRate(); - thread = SDL_CreateThread(streamDataFun, threadName.c_str(), this); + thread = createSDLThread + (this, threadName); } void ALStream::pauseStream() @@ -344,7 +344,7 @@ void ALStream::streamData() resumeStream(); firstBuffer = false; - streamInited = true; + streamInited.set(); } if (threadTermReq) @@ -352,7 +352,7 @@ void ALStream::streamData() if (status == ALDataSource::EndOfStream) { - sourceExhausted = true; + sourceExhausted.set(); break; } } @@ -400,7 +400,7 @@ void ALStream::streamData() if (status == ALDataSource::Error) { - sourceExhausted = true; + sourceExhausted.set(); return; } @@ -419,7 +419,7 @@ void ALStream::streamData() lastBuf = buf; if (status == ALDataSource::EndOfStream) - sourceExhausted = true; + sourceExhausted.set(); } if (threadTermReq) @@ -428,10 +428,3 @@ void ALStream::streamData() SDL_Delay(AUDIO_SLEEP); } } - -int ALStream::streamDataFun(void *_self) -{ - ALStream &self = *static_cast(_self); - self.streamData(); - return 0; -} diff --git a/src/alstream.h b/src/alstream.h index 7de277d..e8c1606 100644 --- a/src/alstream.h +++ b/src/alstream.h @@ -23,12 +23,11 @@ #define ALSTREAM_H #include "al-util.h" +#include "sdl-util.h" #include #include -struct SDL_mutex; -struct SDL_thread; struct ALDataSource; #define STREAM_BUFS 3 @@ -59,12 +58,12 @@ struct ALStream /* When this flag isn't set and alSrc is * in 'STOPPED' state, stream isn't over * (it just hasn't started yet) */ - bool streamInited; - bool sourceExhausted; + AtomicFlag streamInited; + AtomicFlag sourceExhausted; - bool threadTermReq; + AtomicFlag threadTermReq; - bool needsRewind; + AtomicFlag needsRewind; float startOffset; float pitch; @@ -118,7 +117,6 @@ private: /* thread func */ void streamData(); - static int streamDataFun(void *); }; #endif // ALSTREAM_H diff --git a/src/audio.cpp b/src/audio.cpp index dcc0b90..aa16e7d 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -25,6 +25,7 @@ #include "soundemitter.h" #include "sharedstate.h" #include "sharedmidistate.h" +#include "sdl-util.h" #include @@ -55,8 +56,7 @@ struct AudioPrivate struct { SDL_Thread *thread; - bool active; - bool termReq; + AtomicFlag termReq; MeWatchState state; } meWatch; @@ -66,19 +66,18 @@ struct AudioPrivate me(ALStream::NotLooped, "me"), se(conf) { - meWatch.active = false; - meWatch.termReq = false; meWatch.state = MeNotPlaying; - meWatch.thread = SDL_CreateThread(meWatchFun, "audio_mewatch", this); + meWatch.thread = createSDLThread + (this, "audio_mewatch"); } ~AudioPrivate() { - meWatch.termReq = true; + meWatch.termReq.set(); SDL_WaitThread(meWatch.thread, 0); } - void meWatchFunInt() + void meWatchFun() { const float fadeOutStep = 1.f / (200 / AUDIO_SLEEP); const float fadeInStep = 1.f / (1000 / AUDIO_SLEEP); @@ -121,13 +120,13 @@ struct AudioPrivate bgm.lockStream(); - float vol = bgm.extVolume; + float vol = bgm.getVolume(AudioStream::External); vol -= fadeOutStep; if (vol < 0 || bgm.stream.queryState() != ALStream::Playing) { /* Either BGM has fully faded out, or stopped midway. -> MePlaying */ - bgm.setExtVolume1(0); + bgm.setVolume(AudioStream::External, 0); bgm.stream.pause(); meWatch.state = MePlaying; bgm.unlockStream(); @@ -136,7 +135,7 @@ struct AudioPrivate break; } - bgm.setExtVolume1(vol); + bgm.setVolume(AudioStream::External, vol); bgm.unlockStream(); me.unlockStream(); @@ -165,7 +164,7 @@ struct AudioPrivate else { /* BGM is stopped. -> MeNotPlaying */ - bgm.setExtVolume1(1.0); + bgm.setVolume(AudioStream::External, 1.0); if (!bgm.noResumeStop) bgm.stream.play(); @@ -188,7 +187,7 @@ struct AudioPrivate if (bgm.stream.queryState() == ALStream::Stopped) { /* BGM stopped midway fade in. -> MeNotPlaying */ - bgm.setExtVolume1(1.0); + bgm.setVolume(AudioStream::External, 1.0); meWatch.state = MeNotPlaying; bgm.unlockStream(); @@ -208,7 +207,7 @@ struct AudioPrivate break; } - float vol = bgm.extVolume; + float vol = bgm.getVolume(AudioStream::External); vol += fadeInStep; if (vol >= 1) @@ -218,7 +217,7 @@ struct AudioPrivate meWatch.state = MeNotPlaying; } - bgm.setExtVolume1(vol); + bgm.setVolume(AudioStream::External, vol); me.unlockStream(); bgm.unlockStream(); @@ -230,13 +229,6 @@ struct AudioPrivate SDL_Delay(AUDIO_SLEEP); } } - - static int meWatchFun(void *self) - { - static_cast(self)->meWatchFunInt(); - - return 0; - } }; Audio::Audio(const Config &conf) diff --git a/src/audiostream.cpp b/src/audiostream.cpp index d5975e9..16cade7 100644 --- a/src/audiostream.cpp +++ b/src/audiostream.cpp @@ -30,19 +30,21 @@ AudioStream::AudioStream(ALStream::LoopMode loopMode, const std::string &threadId) - : baseVolume(1.0), - fadeVolume(1.0), - extVolume(1.0), - extPaused(false), + : extPaused(false), noResumeStop(false), stream(loopMode, threadId) { current.volume = 1.0; current.pitch = 1.0; - fade.active = false; + for (size_t i = 0; i < VolumeTypeCount; ++i) + volumes[i] = 1.0; + fade.thread = 0; - fade.threadName = std::string("audio_fade (") + threadId + ")"; + fade.threadName = std::string("audio_fadeout (") + threadId + ")"; + + fadeIn.thread = 0; + fadeIn.threadName = std::string("audio_fadein (") + threadId + ")"; streamMut = SDL_CreateMutex(); } @@ -51,10 +53,16 @@ AudioStream::~AudioStream() { if (fade.thread) { - fade.reqTerm = true; + fade.reqTerm.set(); SDL_WaitThread(fade.thread, 0); } + if (fadeIn.thread) + { + fadeIn.rqTerm.set(); + SDL_WaitThread(fadeIn.thread, 0); + } + lockStream(); stream.stop(); @@ -70,7 +78,7 @@ void AudioStream::play(const std::string &filename, int pitch, float offset) { - finiFadeInt(); + finiFadeOutInt(); lockStream(); @@ -96,7 +104,7 @@ void AudioStream::play(const std::string &filename, && _pitch == current.pitch && (sState == ALStream::Playing || sState == ALStream::Paused)) { - setBaseVolume(_volume); + setVolume(Base, _volume); current.volume = _volume; unlockStream(); return; @@ -132,9 +140,15 @@ void AudioStream::play(const std::string &filename, break; } - setBaseVolume(_volume); + setVolume(Base, _volume); stream.setPitch(_pitch); + if (offset > 0) + { + setVolume(FadeIn, 0); + startFadeIn(); + } + current.filename = filename; current.volume = _volume; current.pitch = _pitch; @@ -149,7 +163,7 @@ void AudioStream::play(const std::string &filename, void AudioStream::stop() { - finiFadeInt(); + finiFadeOutInt(); lockStream(); @@ -190,18 +204,19 @@ void AudioStream::fadeOut(int duration) if (fade.thread) { - fade.reqFini = true; + fade.reqFini.set(); SDL_WaitThread(fade.thread, 0); fade.thread = 0; } - fade.active = true; + fade.active.set(); fade.msStep = (1.0) / duration; - fade.reqFini = false; - fade.reqTerm = false; + fade.reqFini.clear(); + fade.reqTerm.clear(); fade.startTicks = SDL_GetTicks(); - fade.thread = SDL_CreateThread(fadeThreadFun, fade.threadName.c_str(), this); + fade.thread = createSDLThread + (this, fade.threadName); unlockStream(); } @@ -219,16 +234,15 @@ void AudioStream::unlockStream() SDL_UnlockMutex(streamMut); } -void AudioStream::setFadeVolume(float value) +void AudioStream::setVolume(VolumeType type, float value) { - fadeVolume = value; + volumes[type] = value; updateVolume(); } -void AudioStream::setExtVolume1(float value) +float AudioStream::getVolume(VolumeType type) { - extVolume = value; - updateVolume(); + return volumes[type]; } float AudioStream::playingOffset() @@ -236,28 +250,47 @@ float AudioStream::playingOffset() return stream.queryOffset(); } -void AudioStream::finiFadeInt() -{ - if (!fade.thread) - return; - - fade.reqFini = true; - SDL_WaitThread(fade.thread, 0); - fade.thread = 0; -} - void AudioStream::updateVolume() { - stream.setVolume(baseVolume * fadeVolume * extVolume); + float vol = 1.0; + + for (size_t i = 0; i < VolumeTypeCount; ++i) + vol *= volumes[i]; + + stream.setVolume(vol); } -void AudioStream::setBaseVolume(float value) +void AudioStream::finiFadeOutInt() { - baseVolume = value; - updateVolume(); + if (fade.thread) + { + fade.reqFini.set(); + SDL_WaitThread(fade.thread, 0); + fade.thread = 0; + } + + if (fadeIn.thread) + { + fadeIn.rqFini.set(); + SDL_WaitThread(fadeIn.thread, 0); + fadeIn.thread = 0; + } } -void AudioStream::fadeThread() +void AudioStream::startFadeIn() +{ + /* Previous fadein should always be terminated in play() */ + assert(!fadeIn.thread); + + fadeIn.rqFini.clear(); + fadeIn.rqTerm.clear(); + fadeIn.startTicks = SDL_GetTicks(); + + fadeIn.thread = createSDLThread + (this, fadeIn.threadName); +} + +void AudioStream::fadeOutThread() { while (true) { @@ -273,31 +306,59 @@ void AudioStream::fadeThread() ALStream::State state = stream.queryState(); if (state != ALStream::Playing - || resVol < 0 - || fade.reqFini) + || resVol < 0 + || fade.reqFini) { if (state != ALStream::Paused) stream.stop(); - setFadeVolume(1.0); + setVolume(FadeOut, 1.0); unlockStream(); break; } - setFadeVolume(resVol); + setVolume(FadeOut, resVol); unlockStream(); SDL_Delay(AUDIO_SLEEP); } - fade.active = false; + fade.active.clear(); } -int AudioStream::fadeThreadFun(void *self) +void AudioStream::fadeInThread() { - static_cast(self)->fadeThread(); + while (true) + { + if (fadeIn.rqTerm) + break; - return 0; + lockStream(); + + /* Fade in duration is always 1 second */ + uint32_t cur = SDL_GetTicks() - fadeIn.startTicks; + float prog = cur / 1000.0; + + ALStream::State state = stream.queryState(); + + if (state != ALStream::Playing + || prog >= 1.0 + || fadeIn.rqFini) + { + setVolume(FadeIn, 1.0); + unlockStream(); + + break; + } + + /* Quadratic increase (not really the same as + * in RMVXA, but close enough) */ + setVolume(FadeIn, prog*prog); + + unlockStream(); + + SDL_Delay(AUDIO_SLEEP); + } } diff --git a/src/audiostream.h b/src/audiostream.h index 79a2509..d968af3 100644 --- a/src/audiostream.h +++ b/src/audiostream.h @@ -24,12 +24,10 @@ #include "al-util.h" #include "alstream.h" +#include "sdl-util.h" #include -struct SDL_mutex; -struct SDL_Thread; - struct AudioStream { struct @@ -39,17 +37,21 @@ struct AudioStream float pitch; } current; - /* Volume set with 'play()' */ - float baseVolume; - - /* Volume set by external threads, + /* Volumes set by external threads, * such as for fade-in/out. - * Multiplied with intVolume for final - * playback volume. - * fadeVolume: used by fade-out thread. - * extVolume: used by MeWatch. */ - float fadeVolume; - float extVolume; + * Multiplied together for final + * playback volume. Used with setVolume(). + * Base is set by play(). + * External is used by MeWatch */ + enum VolumeType + { + Base = 0, + FadeOut, + FadeIn, + External, + + VolumeTypeCount + }; /* Note that 'extPaused' and 'noResumeStop' are * effectively only used with the AudioStream @@ -76,18 +78,19 @@ struct AudioStream ALStream stream; SDL_mutex *streamMut; + /* Fade out */ struct { - /* Fade is in progress */ - bool active; + /* Fade out is in progress */ + AtomicFlag active; /* Request fade thread to finish and * cleanup (like it normally would) */ - bool reqFini; + AtomicFlag reqFini; /* Request fade thread to terminate * immediately */ - bool reqTerm; + AtomicFlag reqTerm; SDL_Thread *thread; std::string threadName; @@ -100,6 +103,18 @@ struct AudioStream uint32_t startTicks; } fade; + /* Fade in */ + struct + { + AtomicFlag rqFini; + AtomicFlag rqTerm; + + SDL_Thread *thread; + std::string threadName; + + uint32_t startTicks; + } fadeIn; + AudioStream(ALStream::LoopMode loopMode, const std::string &threadId); ~AudioStream(); @@ -117,19 +132,20 @@ struct AudioStream void lockStream(); void unlockStream(); - void setFadeVolume(float value); - void setExtVolume1(float value); + void setVolume(VolumeType type, float value); + float getVolume(VolumeType type); float playingOffset(); private: - void finiFadeInt(); - + float volumes[VolumeTypeCount]; void updateVolume(); - void setBaseVolume(float value); - void fadeThread(); - static int fadeThreadFun(void *); + void finiFadeOutInt(); + void startFadeIn(); + + void fadeOutThread(); + void fadeInThread(); }; #endif // AUDIOSTREAM_H diff --git a/src/eventthread.h b/src/eventthread.h index 23402c5..013f60b 100644 --- a/src/eventthread.h +++ b/src/eventthread.h @@ -24,47 +24,20 @@ #include "config.h" #include "etc-internal.h" +#include "sdl-util.h" #include #include #include #include -#include #include #include struct RGSSThreadData; -struct SDL_Thread; struct SDL_Window; -struct AtomicFlag -{ - AtomicFlag() - { - clear(); - } - - void set() - { - SDL_AtomicSet(&atom, 1); - } - - void clear() - { - SDL_AtomicSet(&atom, 0); - } - - operator bool() const - { - return SDL_AtomicGet(&atom); - } - -private: - mutable SDL_atomic_t atom; -}; - class EventThread { public: diff --git a/src/sdl-util.h b/src/sdl-util.h new file mode 100644 index 0000000..ba02046 --- /dev/null +++ b/src/sdl-util.h @@ -0,0 +1,48 @@ +#ifndef SDLUTIL_H +#define SDLUTIL_H + +#include +#include + +#include + +struct AtomicFlag +{ + AtomicFlag() + { + clear(); + } + + void set() + { + SDL_AtomicSet(&atom, 1); + } + + void clear() + { + SDL_AtomicSet(&atom, 0); + } + + operator bool() const + { + return SDL_AtomicGet(&atom); + } + +private: + mutable SDL_atomic_t atom; +}; + +template +int __sdlThreadFun(void *obj) +{ + (static_cast(obj)->*func)(); + return 0; +} + +template +SDL_Thread *createSDLThread(C *obj, const std::string &name = std::string()) +{ + return SDL_CreateThread(__sdlThreadFun, name.c_str(), obj); +} + +#endif // SDLUTIL_H