From 5fca94616cf8d7a2051e02d8ed0d03e2c4786aca Mon Sep 17 00:00:00 2001
From: Jonas Kulla <Nyocurio@gmail.com>
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;
 };