From 5fca94616cf8d7a2051e02d8ed0d03e2c4786aca Mon Sep 17 00:00:00 2001 From: Jonas Kulla Date: Thu, 5 Dec 2013 00:24:18 +0100 Subject: [PATCH] Audio: Implement RGSS3 functionality 'Pos' and 'Play' functions take a float for their 'pos' parameter, which is in seconds of playback. 'setupMidi' is a noop at this point. --- src/audio.cpp | 110 +++++++++++++++++++++++++++++++++++++++----------- src/audio.h | 40 ++++++++++++++++-- 2 files changed, 123 insertions(+), 27 deletions(-) diff --git a/src/audio.cpp b/src/audio.cpp index c2aa840..bffbcaf 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -556,7 +556,14 @@ struct VorbisSource : ALDataSource void seekToOffset(float seconds) { - ov_time_seek(&vf, seconds); + currentFrame = seconds * info.rate; + + if (loop.valid && currentFrame > loop.end) + currentFrame = loop.start; + + /* If seeking fails, just seek back to start */ + if (ov_pcm_seek(&vf, currentFrame) != 0) + ov_raw_seek(&vf, 0); } Status fillBuffer(AL::Buffer::ID alBuffer) @@ -569,6 +576,8 @@ struct VorbisSource : ALDataSource Status retStatus = ALDataSource::NoError; + bool readAgain = false; + if (loop.valid) { int tilLoopEnd = loop.end * info.frameSize; @@ -602,7 +611,22 @@ struct VorbisSource : ALDataSource retStatus = ALDataSource::EndOfStream; } - break; + /* If we sought right to the end of the file, + * we might be EOF without actually having read + * any data at all yet (which mustn't happen), + * so we try to continue reading some data. */ + if (bufUsed > 0) + break; + + if (readAgain) + { + /* We're still not getting data though. + * Just error out to prevent an endless loop */ + retStatus = ALDataSource::Error; + break; + } + + readAgain = true; } bufUsed += (res / sizeof(int16_t)); @@ -682,6 +706,7 @@ struct ALStream bool threadTermReq; bool needsRewind; + float startOffset; AL::Source::ID alSrc; AL::Buffer::ID alBuf[streamBufs]; @@ -790,7 +815,7 @@ struct ALStream state = Stopped; } - void play() + void play(float offset = 0) { checkStopped(); @@ -800,7 +825,7 @@ struct ALStream case Playing: return; case Stopped: - startStream(); + startStream(offset); break; case Paused : resumeStream(); @@ -826,15 +851,6 @@ struct ALStream state = Paused; } - void setOffset(float value) - { - if (state == Closed) - return; - // XXX needs more work. protect source with mutex - source->seekToOffset(value); - needsRewind = false; - } - void setVolume(float value) { AL::Source::setVolume(alSrc, value); @@ -907,16 +923,18 @@ private: procFrames = 0; } - void startStream() + void startStream(float offset) { clearALQueue(); - procFrames = 0; - preemptPause = false; streamInited = false; sourceExhausted = false; threadTermReq = false; + + startOffset = offset; + procFrames = offset * source->sampleRate(); + thread = SDL_CreateThread(streamDataFun, "al_stream", this); } @@ -988,7 +1006,12 @@ private: ALDataSource::Status status; if (needsRewind) - source->reset(); + { + if (startOffset > 0) + source->seekToOffset(startOffset); + else + source->reset(); + } for (int i = 0; i < streamBufs; ++i) { @@ -1177,7 +1200,8 @@ struct AudioStream void play(const std::string &filename, int volume, - int pitch) + int pitch, + float offset = 0) { finiFadeInt(); @@ -1236,7 +1260,7 @@ struct AudioStream current.pitch = _pitch; if (!extPaused) - stream.play(); + stream.play(offset); unlockStream(); } @@ -1323,6 +1347,11 @@ struct AudioStream updateVolume(); } + float playingOffset() + { + return stream.queryOffset(); + } + private: void finiFadeInt() { @@ -1600,10 +1629,18 @@ Audio::Audio() void Audio::bgmPlay(const char *filename, - int volume, - int pitch) + int volume, + int pitch + #ifdef RGSS3 + ,float pos + #endif + ) { +#ifdef RGSS3 + p->bgm.play(filename, volume, pitch, pos); +#else p->bgm.play(filename, volume, pitch); +#endif } void Audio::bgmStop() @@ -1618,10 +1655,18 @@ void Audio::bgmFade(int time) void Audio::bgsPlay(const char *filename, - int volume, - int pitch) + int volume, + int pitch + #ifdef RGSS3 + ,float pos + #endif + ) { +#ifdef RGSS3 + p->bgs.play(filename, volume, pitch, pos); +#else p->bgs.play(filename, volume, pitch); +#endif } void Audio::bgsStop() @@ -1665,4 +1710,23 @@ void Audio::seStop() p->se.stop(); } +#ifdef RGSS3 + +void Audio::setupMidi() +{ + +} + +float Audio::bgmPos() +{ + return p->bgm.playingOffset(); +} + +float Audio::bgsPos() +{ + return p->bgs.playingOffset(); +} + +#endif + Audio::~Audio() { delete p; } diff --git a/src/audio.h b/src/audio.h index 712cf5d..3efc2c0 100644 --- a/src/audio.h +++ b/src/audio.h @@ -22,6 +22,16 @@ #ifndef AUDIO_H #define AUDIO_H +/* Concerning the 'pos' parameter: + * RGSS3 actually doesn't specify a format for this, + * it's only implied that it is a numerical value + * (must be 0 on invalid cases), and it's not used for + * anything outside passing it back into bgm/bgs_play. + * We use this freedom to define pos to be a float, + * in seconds of playback. (RGSS3 seems to use large + * integers that _look_ like sample offsets but I can't + * quite make out their meaning yet) */ + struct AudioPrivate; class Audio @@ -30,21 +40,43 @@ public: Audio(); ~Audio(); - void bgmPlay(const char *filename, int volume = 100, int pitch = 100); + void bgmPlay(const char *filename, + int volume = 100, + int pitch = 100 +#ifdef RGSS3 + ,float pos = 0 +#endif + ); void bgmStop(); void bgmFade(int time); - void bgsPlay(const char *filename, int volume = 100, int pitch = 100); + void bgsPlay(const char *filename, + int volume = 100, + int pitch = 100 +#ifdef RGSS3 + ,float pos = 0 +#endif + ); void bgsStop(); void bgsFade(int time); - void mePlay(const char *filename, int volume = 100, int pitch = 100); + void mePlay(const char *filename, + int volume = 100, + int pitch = 100); void meStop(); void meFade(int time); - void sePlay(const char *filename, int volume = 100, int pitch = 100); + void sePlay(const char *filename, + int volume = 100, + int pitch = 100); void seStop(); +#ifdef RGSS3 + void setupMidi(); + float bgmPos(); + float bgsPos(); +#endif + private: AudioPrivate *p; };