Audio: Clean up threading and add AudioStream fadein (RGSS3)

This commit is contained in:
Jonas Kulla 2014-09-30 09:13:12 +02:00
parent 46497eae01
commit 51a0f3903c
10 changed files with 228 additions and 146 deletions

View File

@ -153,6 +153,7 @@ set(MAIN_HEADERS
src/tileatlasvx.h src/tileatlasvx.h
src/sharedmidistate.h src/sharedmidistate.h
src/fluid-fun.h src/fluid-fun.h
src/sdl-util.h
) )
set(MAIN_SOURCE set(MAIN_SOURCE

View File

@ -11,7 +11,6 @@ Missing RGSS3 functionality:
* Text outline * Text outline
* Movie playback * Movie playback
* Audio fade-in
Some other things might be implemented, but simply not bound yet. Some other things might be implemented, but simply not bound yet.

View File

@ -131,7 +131,8 @@ HEADERS += \
src/tilemapvx.h \ src/tilemapvx.h \
src/tileatlasvx.h \ src/tileatlasvx.h \
src/sharedmidistate.h \ src/sharedmidistate.h \
src/fluid-fun.h src/fluid-fun.h \
src/sdl-util.h
SOURCES += \ SOURCES += \
src/main.cpp \ src/main.cpp \

View File

@ -26,6 +26,7 @@
#include "filesystem.h" #include "filesystem.h"
#include "aldatasource.h" #include "aldatasource.h"
#include "fluid-fun.h" #include "fluid-fun.h"
#include "sdl-util.h"
#include <SDL_mutex.h> #include <SDL_mutex.h>
#include <SDL_thread.h> #include <SDL_thread.h>
@ -38,8 +39,6 @@ ALStream::ALStream(LoopMode loopMode,
source(0), source(0),
thread(0), thread(0),
preemptPause(false), preemptPause(false),
streamInited(false),
needsRewind(false),
pitch(1.0) pitch(1.0)
{ {
alSrc = AL::Source::gen(); alSrc = AL::Source::gen();
@ -198,7 +197,7 @@ void ALStream::openSource(const std::string &filename)
{ {
const char *ext; const char *ext;
shState->fileSystem().openRead(srcOps, filename.c_str(), FileSystem::Audio, false, &ext); shState->fileSystem().openRead(srcOps, filename.c_str(), FileSystem::Audio, false, &ext);
needsRewind = false; needsRewind.clear();
/* Try to read ogg file signature */ /* Try to read ogg file signature */
char sig[5] = { 0 }; char sig[5] = { 0 };
@ -227,13 +226,13 @@ void ALStream::openSource(const std::string &filename)
void ALStream::stopStream() void ALStream::stopStream()
{ {
threadTermReq = true; threadTermReq.set();
if (thread) if (thread)
{ {
SDL_WaitThread(thread, 0); SDL_WaitThread(thread, 0);
thread = 0; thread = 0;
needsRewind = true; needsRewind.set();
} }
/* Need to stop the source _after_ the thread has terminated, /* Need to stop the source _after_ the thread has terminated,
@ -249,14 +248,15 @@ void ALStream::startStream(float offset)
AL::Source::clearQueue(alSrc); AL::Source::clearQueue(alSrc);
preemptPause = false; preemptPause = false;
streamInited = false; streamInited.clear();
sourceExhausted = false; sourceExhausted.clear();
threadTermReq = false; threadTermReq.clear();
startOffset = offset; startOffset = offset;
procFrames = offset * source->sampleRate(); procFrames = offset * source->sampleRate();
thread = SDL_CreateThread(streamDataFun, threadName.c_str(), this); thread = createSDLThread
<ALStream, &ALStream::streamData>(this, threadName);
} }
void ALStream::pauseStream() void ALStream::pauseStream()
@ -344,7 +344,7 @@ void ALStream::streamData()
resumeStream(); resumeStream();
firstBuffer = false; firstBuffer = false;
streamInited = true; streamInited.set();
} }
if (threadTermReq) if (threadTermReq)
@ -352,7 +352,7 @@ void ALStream::streamData()
if (status == ALDataSource::EndOfStream) if (status == ALDataSource::EndOfStream)
{ {
sourceExhausted = true; sourceExhausted.set();
break; break;
} }
} }
@ -400,7 +400,7 @@ void ALStream::streamData()
if (status == ALDataSource::Error) if (status == ALDataSource::Error)
{ {
sourceExhausted = true; sourceExhausted.set();
return; return;
} }
@ -419,7 +419,7 @@ void ALStream::streamData()
lastBuf = buf; lastBuf = buf;
if (status == ALDataSource::EndOfStream) if (status == ALDataSource::EndOfStream)
sourceExhausted = true; sourceExhausted.set();
} }
if (threadTermReq) if (threadTermReq)
@ -428,10 +428,3 @@ void ALStream::streamData()
SDL_Delay(AUDIO_SLEEP); SDL_Delay(AUDIO_SLEEP);
} }
} }
int ALStream::streamDataFun(void *_self)
{
ALStream &self = *static_cast<ALStream*>(_self);
self.streamData();
return 0;
}

View File

@ -23,12 +23,11 @@
#define ALSTREAM_H #define ALSTREAM_H
#include "al-util.h" #include "al-util.h"
#include "sdl-util.h"
#include <string> #include <string>
#include <SDL_rwops.h> #include <SDL_rwops.h>
struct SDL_mutex;
struct SDL_thread;
struct ALDataSource; struct ALDataSource;
#define STREAM_BUFS 3 #define STREAM_BUFS 3
@ -59,12 +58,12 @@ struct ALStream
/* When this flag isn't set and alSrc is /* When this flag isn't set and alSrc is
* in 'STOPPED' state, stream isn't over * in 'STOPPED' state, stream isn't over
* (it just hasn't started yet) */ * (it just hasn't started yet) */
bool streamInited; AtomicFlag streamInited;
bool sourceExhausted; AtomicFlag sourceExhausted;
bool threadTermReq; AtomicFlag threadTermReq;
bool needsRewind; AtomicFlag needsRewind;
float startOffset; float startOffset;
float pitch; float pitch;
@ -118,7 +117,6 @@ private:
/* thread func */ /* thread func */
void streamData(); void streamData();
static int streamDataFun(void *);
}; };
#endif // ALSTREAM_H #endif // ALSTREAM_H

View File

@ -25,6 +25,7 @@
#include "soundemitter.h" #include "soundemitter.h"
#include "sharedstate.h" #include "sharedstate.h"
#include "sharedmidistate.h" #include "sharedmidistate.h"
#include "sdl-util.h"
#include <string> #include <string>
@ -55,8 +56,7 @@ struct AudioPrivate
struct struct
{ {
SDL_Thread *thread; SDL_Thread *thread;
bool active; AtomicFlag termReq;
bool termReq;
MeWatchState state; MeWatchState state;
} meWatch; } meWatch;
@ -66,19 +66,18 @@ struct AudioPrivate
me(ALStream::NotLooped, "me"), me(ALStream::NotLooped, "me"),
se(conf) se(conf)
{ {
meWatch.active = false;
meWatch.termReq = false;
meWatch.state = MeNotPlaying; meWatch.state = MeNotPlaying;
meWatch.thread = SDL_CreateThread(meWatchFun, "audio_mewatch", this); meWatch.thread = createSDLThread
<AudioPrivate, &AudioPrivate::meWatchFun>(this, "audio_mewatch");
} }
~AudioPrivate() ~AudioPrivate()
{ {
meWatch.termReq = true; meWatch.termReq.set();
SDL_WaitThread(meWatch.thread, 0); SDL_WaitThread(meWatch.thread, 0);
} }
void meWatchFunInt() void meWatchFun()
{ {
const float fadeOutStep = 1.f / (200 / AUDIO_SLEEP); const float fadeOutStep = 1.f / (200 / AUDIO_SLEEP);
const float fadeInStep = 1.f / (1000 / AUDIO_SLEEP); const float fadeInStep = 1.f / (1000 / AUDIO_SLEEP);
@ -121,13 +120,13 @@ struct AudioPrivate
bgm.lockStream(); bgm.lockStream();
float vol = bgm.extVolume; float vol = bgm.getVolume(AudioStream::External);
vol -= fadeOutStep; vol -= fadeOutStep;
if (vol < 0 || bgm.stream.queryState() != ALStream::Playing) if (vol < 0 || bgm.stream.queryState() != ALStream::Playing)
{ {
/* Either BGM has fully faded out, or stopped midway. -> MePlaying */ /* Either BGM has fully faded out, or stopped midway. -> MePlaying */
bgm.setExtVolume1(0); bgm.setVolume(AudioStream::External, 0);
bgm.stream.pause(); bgm.stream.pause();
meWatch.state = MePlaying; meWatch.state = MePlaying;
bgm.unlockStream(); bgm.unlockStream();
@ -136,7 +135,7 @@ struct AudioPrivate
break; break;
} }
bgm.setExtVolume1(vol); bgm.setVolume(AudioStream::External, vol);
bgm.unlockStream(); bgm.unlockStream();
me.unlockStream(); me.unlockStream();
@ -165,7 +164,7 @@ struct AudioPrivate
else else
{ {
/* BGM is stopped. -> MeNotPlaying */ /* BGM is stopped. -> MeNotPlaying */
bgm.setExtVolume1(1.0); bgm.setVolume(AudioStream::External, 1.0);
if (!bgm.noResumeStop) if (!bgm.noResumeStop)
bgm.stream.play(); bgm.stream.play();
@ -188,7 +187,7 @@ struct AudioPrivate
if (bgm.stream.queryState() == ALStream::Stopped) if (bgm.stream.queryState() == ALStream::Stopped)
{ {
/* BGM stopped midway fade in. -> MeNotPlaying */ /* BGM stopped midway fade in. -> MeNotPlaying */
bgm.setExtVolume1(1.0); bgm.setVolume(AudioStream::External, 1.0);
meWatch.state = MeNotPlaying; meWatch.state = MeNotPlaying;
bgm.unlockStream(); bgm.unlockStream();
@ -208,7 +207,7 @@ struct AudioPrivate
break; break;
} }
float vol = bgm.extVolume; float vol = bgm.getVolume(AudioStream::External);
vol += fadeInStep; vol += fadeInStep;
if (vol >= 1) if (vol >= 1)
@ -218,7 +217,7 @@ struct AudioPrivate
meWatch.state = MeNotPlaying; meWatch.state = MeNotPlaying;
} }
bgm.setExtVolume1(vol); bgm.setVolume(AudioStream::External, vol);
me.unlockStream(); me.unlockStream();
bgm.unlockStream(); bgm.unlockStream();
@ -230,13 +229,6 @@ struct AudioPrivate
SDL_Delay(AUDIO_SLEEP); SDL_Delay(AUDIO_SLEEP);
} }
} }
static int meWatchFun(void *self)
{
static_cast<AudioPrivate*>(self)->meWatchFunInt();
return 0;
}
}; };
Audio::Audio(const Config &conf) Audio::Audio(const Config &conf)

View File

@ -30,19 +30,21 @@
AudioStream::AudioStream(ALStream::LoopMode loopMode, AudioStream::AudioStream(ALStream::LoopMode loopMode,
const std::string &threadId) const std::string &threadId)
: baseVolume(1.0), : extPaused(false),
fadeVolume(1.0),
extVolume(1.0),
extPaused(false),
noResumeStop(false), noResumeStop(false),
stream(loopMode, threadId) stream(loopMode, threadId)
{ {
current.volume = 1.0; current.volume = 1.0;
current.pitch = 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.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(); streamMut = SDL_CreateMutex();
} }
@ -51,10 +53,16 @@ AudioStream::~AudioStream()
{ {
if (fade.thread) if (fade.thread)
{ {
fade.reqTerm = true; fade.reqTerm.set();
SDL_WaitThread(fade.thread, 0); SDL_WaitThread(fade.thread, 0);
} }
if (fadeIn.thread)
{
fadeIn.rqTerm.set();
SDL_WaitThread(fadeIn.thread, 0);
}
lockStream(); lockStream();
stream.stop(); stream.stop();
@ -70,7 +78,7 @@ void AudioStream::play(const std::string &filename,
int pitch, int pitch,
float offset) float offset)
{ {
finiFadeInt(); finiFadeOutInt();
lockStream(); lockStream();
@ -96,7 +104,7 @@ void AudioStream::play(const std::string &filename,
&& _pitch == current.pitch && _pitch == current.pitch
&& (sState == ALStream::Playing || sState == ALStream::Paused)) && (sState == ALStream::Playing || sState == ALStream::Paused))
{ {
setBaseVolume(_volume); setVolume(Base, _volume);
current.volume = _volume; current.volume = _volume;
unlockStream(); unlockStream();
return; return;
@ -132,9 +140,15 @@ void AudioStream::play(const std::string &filename,
break; break;
} }
setBaseVolume(_volume); setVolume(Base, _volume);
stream.setPitch(_pitch); stream.setPitch(_pitch);
if (offset > 0)
{
setVolume(FadeIn, 0);
startFadeIn();
}
current.filename = filename; current.filename = filename;
current.volume = _volume; current.volume = _volume;
current.pitch = _pitch; current.pitch = _pitch;
@ -149,7 +163,7 @@ void AudioStream::play(const std::string &filename,
void AudioStream::stop() void AudioStream::stop()
{ {
finiFadeInt(); finiFadeOutInt();
lockStream(); lockStream();
@ -190,18 +204,19 @@ void AudioStream::fadeOut(int duration)
if (fade.thread) if (fade.thread)
{ {
fade.reqFini = true; fade.reqFini.set();
SDL_WaitThread(fade.thread, 0); SDL_WaitThread(fade.thread, 0);
fade.thread = 0; fade.thread = 0;
} }
fade.active = true; fade.active.set();
fade.msStep = (1.0) / duration; fade.msStep = (1.0) / duration;
fade.reqFini = false; fade.reqFini.clear();
fade.reqTerm = false; fade.reqTerm.clear();
fade.startTicks = SDL_GetTicks(); fade.startTicks = SDL_GetTicks();
fade.thread = SDL_CreateThread(fadeThreadFun, fade.threadName.c_str(), this); fade.thread = createSDLThread
<AudioStream, &AudioStream::fadeOutThread>(this, fade.threadName);
unlockStream(); unlockStream();
} }
@ -219,16 +234,15 @@ void AudioStream::unlockStream()
SDL_UnlockMutex(streamMut); SDL_UnlockMutex(streamMut);
} }
void AudioStream::setFadeVolume(float value) void AudioStream::setVolume(VolumeType type, float value)
{ {
fadeVolume = value; volumes[type] = value;
updateVolume(); updateVolume();
} }
void AudioStream::setExtVolume1(float value) float AudioStream::getVolume(VolumeType type)
{ {
extVolume = value; return volumes[type];
updateVolume();
} }
float AudioStream::playingOffset() float AudioStream::playingOffset()
@ -236,28 +250,47 @@ float AudioStream::playingOffset()
return stream.queryOffset(); return stream.queryOffset();
} }
void AudioStream::finiFadeInt()
{
if (!fade.thread)
return;
fade.reqFini = true;
SDL_WaitThread(fade.thread, 0);
fade.thread = 0;
}
void AudioStream::updateVolume() 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; if (fade.thread)
updateVolume(); {
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
<AudioStream, &AudioStream::fadeInThread>(this, fadeIn.threadName);
}
void AudioStream::fadeOutThread()
{ {
while (true) while (true)
{ {
@ -279,25 +312,53 @@ void AudioStream::fadeThread()
if (state != ALStream::Paused) if (state != ALStream::Paused)
stream.stop(); stream.stop();
setFadeVolume(1.0); setVolume(FadeOut, 1.0);
unlockStream(); unlockStream();
break; break;
} }
setFadeVolume(resVol); setVolume(FadeOut, resVol);
unlockStream(); unlockStream();
SDL_Delay(AUDIO_SLEEP); SDL_Delay(AUDIO_SLEEP);
} }
fade.active = false; fade.active.clear();
} }
int AudioStream::fadeThreadFun(void *self) void AudioStream::fadeInThread()
{ {
static_cast<AudioStream*>(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);
}
} }

View File

@ -24,12 +24,10 @@
#include "al-util.h" #include "al-util.h"
#include "alstream.h" #include "alstream.h"
#include "sdl-util.h"
#include <string> #include <string>
struct SDL_mutex;
struct SDL_Thread;
struct AudioStream struct AudioStream
{ {
struct struct
@ -39,17 +37,21 @@ struct AudioStream
float pitch; float pitch;
} current; } current;
/* Volume set with 'play()' */ /* Volumes set by external threads,
float baseVolume;
/* Volume set by external threads,
* such as for fade-in/out. * such as for fade-in/out.
* Multiplied with intVolume for final * Multiplied together for final
* playback volume. * playback volume. Used with setVolume().
* fadeVolume: used by fade-out thread. * Base is set by play().
* extVolume: used by MeWatch. */ * External is used by MeWatch */
float fadeVolume; enum VolumeType
float extVolume; {
Base = 0,
FadeOut,
FadeIn,
External,
VolumeTypeCount
};
/* Note that 'extPaused' and 'noResumeStop' are /* Note that 'extPaused' and 'noResumeStop' are
* effectively only used with the AudioStream * effectively only used with the AudioStream
@ -76,18 +78,19 @@ struct AudioStream
ALStream stream; ALStream stream;
SDL_mutex *streamMut; SDL_mutex *streamMut;
/* Fade out */
struct struct
{ {
/* Fade is in progress */ /* Fade out is in progress */
bool active; AtomicFlag active;
/* Request fade thread to finish and /* Request fade thread to finish and
* cleanup (like it normally would) */ * cleanup (like it normally would) */
bool reqFini; AtomicFlag reqFini;
/* Request fade thread to terminate /* Request fade thread to terminate
* immediately */ * immediately */
bool reqTerm; AtomicFlag reqTerm;
SDL_Thread *thread; SDL_Thread *thread;
std::string threadName; std::string threadName;
@ -100,6 +103,18 @@ struct AudioStream
uint32_t startTicks; uint32_t startTicks;
} fade; } fade;
/* Fade in */
struct
{
AtomicFlag rqFini;
AtomicFlag rqTerm;
SDL_Thread *thread;
std::string threadName;
uint32_t startTicks;
} fadeIn;
AudioStream(ALStream::LoopMode loopMode, AudioStream(ALStream::LoopMode loopMode,
const std::string &threadId); const std::string &threadId);
~AudioStream(); ~AudioStream();
@ -117,19 +132,20 @@ struct AudioStream
void lockStream(); void lockStream();
void unlockStream(); void unlockStream();
void setFadeVolume(float value); void setVolume(VolumeType type, float value);
void setExtVolume1(float value); float getVolume(VolumeType type);
float playingOffset(); float playingOffset();
private: private:
void finiFadeInt(); float volumes[VolumeTypeCount];
void updateVolume(); void updateVolume();
void setBaseVolume(float value);
void fadeThread(); void finiFadeOutInt();
static int fadeThreadFun(void *); void startFadeIn();
void fadeOutThread();
void fadeInThread();
}; };
#endif // AUDIOSTREAM_H #endif // AUDIOSTREAM_H

View File

@ -24,47 +24,20 @@
#include "config.h" #include "config.h"
#include "etc-internal.h" #include "etc-internal.h"
#include "sdl-util.h"
#include <SDL_scancode.h> #include <SDL_scancode.h>
#include <SDL_joystick.h> #include <SDL_joystick.h>
#include <SDL_mouse.h> #include <SDL_mouse.h>
#include <SDL_mutex.h> #include <SDL_mutex.h>
#include <SDL_atomic.h>
#include <string> #include <string>
#include <stdint.h> #include <stdint.h>
struct RGSSThreadData; struct RGSSThreadData;
struct SDL_Thread;
struct SDL_Window; 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 class EventThread
{ {
public: public:

48
src/sdl-util.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef SDLUTIL_H
#define SDLUTIL_H
#include <SDL_atomic.h>
#include <SDL_thread.h>
#include <string>
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<class C, void (C::*func)()>
int __sdlThreadFun(void *obj)
{
(static_cast<C*>(obj)->*func)();
return 0;
}
template<class C, void (C::*func)()>
SDL_Thread *createSDLThread(C *obj, const std::string &name = std::string())
{
return SDL_CreateThread(__sdlThreadFun<C, func>, name.c_str(), obj);
}
#endif // SDLUTIL_H