From 55e83078dea8256ff164d85dc6f366ddacd84b1e Mon Sep 17 00:00:00 2001
From: Varun Patil <radialapps@gmail.com>
Date: Wed, 8 May 2019 20:45:00 +0530
Subject: [PATCH] Add audio async for SE, disable AudioStream

---
 src/audiostream.cpp  |   3 ++
 src/soundemitter.cpp | 112 +++++++++++++++++++++++++++++++++++++------
 src/soundemitter.h   |   9 +++-
 3 files changed, 108 insertions(+), 16 deletions(-)

diff --git a/src/audiostream.cpp b/src/audiostream.cpp
index af58938..7f88627 100644
--- a/src/audiostream.cpp
+++ b/src/audiostream.cpp
@@ -82,6 +82,9 @@ void AudioStream::play(const std::string &filename,
                        int pitch,
                        float offset)
 {
+#ifdef __EMSCRIPTEN__
+	return;
+#endif
 	finiFadeOutInt();
 
 	lockStream();
diff --git a/src/soundemitter.cpp b/src/soundemitter.cpp
index 74eaf03..c02083b 100644
--- a/src/soundemitter.cpp
+++ b/src/soundemitter.cpp
@@ -30,6 +30,13 @@
 
 #include <SDL_sound.h>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten.h>
+#include <emscripten/fetch.h>
+#include <string>
+#include <fstream>
+#endif
+
 #define SE_CACHE_MEM (10*1024*1024) // 10 MB
 
 struct SoundBuffer
@@ -119,14 +126,21 @@ SoundEmitter::~SoundEmitter()
 		SoundBuffer::deref(iter->second);
 }
 
-void SoundEmitter::play(const std::string &filename,
+struct PlayInternalCallbackData {
+	SoundEmitter * se;
+	int volume;
+	int pitch;
+};
+
+void SoundEmitter::play_internal(const std::string &filename,
                         int volume,
-                        int pitch)
+                        int pitch,
+			emscripten_fetch_t * fetch)
 {
 	float _volume = clamp<int>(volume, 0, 100) / 100.0f;
 	float _pitch  = clamp<int>(pitch, 50, 150) / 100.0f;
 
-	SoundBuffer *buffer = allocateBuffer(filename);
+	SoundBuffer *buffer = allocateBuffer(filename, fetch);
 
 	if (!buffer)
 		return;
@@ -178,6 +192,57 @@ void SoundEmitter::play(const std::string &filename,
 	AL::Source::play(src);
 }
 
+void saveAudioFile(void * arg) {
+	emscripten_fetch_t * fetch = (emscripten_fetch_t *) arg;
+		/*std::string fname(fetch->url);
+		for (size_t i = 0; i < fname.size(); i++) {
+			if (fname[i] == '/') fname[i] = '_';
+		}
+		fname = fname + ".ogg";*/
+	try {
+		PlayInternalCallbackData * data = ((PlayInternalCallbackData *) fetch->userData);
+		data->se->play_internal(fetch->url, data->volume, data->pitch, fetch);
+	} catch (const Exception &e) {}
+	delete ((PlayInternalCallbackData *) fetch->userData);
+	emscripten_fetch_close(fetch);
+}
+
+void audioDownloadSucceeded(emscripten_fetch_t *fetch) {
+	emscripten_push_main_loop_blocker(saveAudioFile, (void *) fetch);
+}
+
+void audioDownloadFailed(emscripten_fetch_t *fetch) {
+	printf("Downloading %s failed, HTTP failure status code: %d.\n", fetch->url, fetch->status);
+	delete ((PlayInternalCallbackData *) fetch->userData);
+	emscripten_fetch_close(fetch);
+}
+
+void addAudioDownload(const char * filename, PlayInternalCallbackData * se) {
+	const char *pfx = "async/";
+	char result[512];
+	strcpy(result, pfx);
+	strcat(result, filename);
+
+	emscripten_fetch_attr_t attr;
+	emscripten_fetch_attr_init(&attr);
+	strcpy(attr.requestMethod, "GET");
+	attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
+	attr.onsuccess = audioDownloadSucceeded;
+	attr.onerror = audioDownloadFailed;
+	attr.userData = se;
+	emscripten_fetch(&attr, result);
+}
+
+void SoundEmitter::play(const std::string &filename,
+                        int volume,
+                        int pitch) {
+	PlayInternalCallbackData * data = new PlayInternalCallbackData();
+	data->se = this;
+	data->volume = volume;
+	data->pitch = pitch;
+	addAudioDownload(filename.c_str(), data);
+}
+
 void SoundEmitter::stop()
 {
 	for (size_t i = 0; i < srcCount; i++)
@@ -192,16 +257,7 @@ struct SoundOpenHandler : FileSystem::OpenHandler
 	    : buffer(0)
 	{}
 
-	bool tryRead(SDL_RWops &ops, const char *ext)
-	{
-		Sound_Sample *sample = Sound_NewSample(&ops, ext, 0, STREAM_BUF_SIZE);
-
-		if (!sample)
-		{
-			SDL_RWclose(&ops);
-			return false;
-		}
-
+	void decode(Sound_Sample *sample) {
 		/* Do all of the decoding in the handler so we don't have
 		 * to keep the source ops around */
 		uint32_t decBytes = Sound_DecodeAll(sample);
@@ -217,12 +273,38 @@ struct SoundOpenHandler : FileSystem::OpenHandler
 							   buffer->bytes, sample->actual.rate);
 
 		Sound_FreeSample(sample);
+	}
+
+	bool tryRead(SDL_RWops &ops, const char *ext)
+	{
+		Sound_Sample *sample = Sound_NewSample(&ops, ext, 0, STREAM_BUF_SIZE);
+
+		if (!sample)
+		{
+			SDL_RWclose(&ops);
+			return false;
+		}
+
+		decode(sample);
+
+		return true;
+	}
+
+	bool emread(emscripten_fetch_t *fetch) {
+		Sound_Sample *sample = Sound_NewSample(SDL_RWFromConstMem(fetch->data, fetch->numBytes), "ogg", 0, STREAM_BUF_SIZE);
+
+		if (!sample)
+		{
+			return false;
+		}
+
+		decode(sample);
 
 		return true;
 	}
 };
 
-SoundBuffer *SoundEmitter::allocateBuffer(const std::string &filename)
+SoundBuffer *SoundEmitter::allocateBuffer(const std::string &filename, emscripten_fetch_t *fetch)
 {
 	SoundBuffer *buffer = bufferHash.value(filename, 0);
 
@@ -239,7 +321,7 @@ SoundBuffer *SoundEmitter::allocateBuffer(const std::string &filename)
 	{
 		/* Buffer not in cache, needs to be loaded */
 		SoundOpenHandler handler;
-		shState->fileSystem().openRead(handler, filename.c_str());
+		handler.emread(fetch);
 		buffer = handler.buffer;
 
 		if (!buffer)
diff --git a/src/soundemitter.h b/src/soundemitter.h
index a340e99..907d366 100644
--- a/src/soundemitter.h
+++ b/src/soundemitter.h
@@ -29,6 +29,10 @@
 #include <string>
 #include <vector>
 
+#ifdef __EMSCRIPTEN__
+#include <emscripten/fetch.h>
+#endif
+
 struct SoundBuffer;
 struct Config;
 
@@ -55,11 +59,14 @@ struct SoundEmitter
 	void play(const std::string &filename,
 	          int volume,
 	          int pitch);
+	void play_internal(const std::string &filename,
+	          int volume,
+	          int pitch, emscripten_fetch_t *fetch);
 
 	void stop();
 
 private:
-	SoundBuffer *allocateBuffer(const std::string &filename);
+	SoundBuffer *allocateBuffer(const std::string &filename, emscripten_fetch_t *fetch);
 };
 
 #endif // SOUNDEMITTER_H