diff --git a/Dengine.vcxproj b/Dengine.vcxproj
index b2030e8..2c138a2 100644
--- a/Dengine.vcxproj
+++ b/Dengine.vcxproj
@@ -121,6 +121,7 @@
+
@@ -139,6 +140,7 @@
+
diff --git a/Dengine.vcxproj.filters b/Dengine.vcxproj.filters
index 10360f9..d14e0fc 100644
--- a/Dengine.vcxproj.filters
+++ b/Dengine.vcxproj.filters
@@ -45,6 +45,9 @@
Source Files
+
+ Source Files
+
@@ -95,5 +98,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/src/AssetManager.c b/src/AssetManager.c
index 71b0909..b1aced2 100644
--- a/src/AssetManager.c
+++ b/src/AssetManager.c
@@ -1,3 +1,5 @@
+#define _CRT_SECURE_NO_WARNINGS
+
#define STBI_FAILURE_USERMSG
#define STB_IMAGE_IMPLEMENTATION
#include
@@ -6,17 +8,32 @@
#define STB_TRUETYPE_IMPLEMENTATION
#include
-#include "Dengine/AssetManager.h"
-#include "Dengine/Debug.h"
-#include "Dengine/OpenGL.h"
-#include "Dengine/Platform.h"
-
//#define WT_RENDER_FONT_FILE
#ifdef WT_RENDER_FONT_FILE
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include
#endif
+#include
+
+#include "Dengine/AssetManager.h"
+#include "Dengine/Debug.h"
+#include "Dengine/OpenGL.h"
+#include "Dengine/Platform.h"
+
+AudioVorbis *asset_getVorbis(AssetManager *assetManager,
+ const enum AudioList type)
+{
+ if (type < audiolist_count)
+ return &assetManager->audio[type];
+
+#ifdef DENGINE_DEBUG
+ ASSERT(INVALID_CODE_PATH);
+#endif
+
+ return NULL;
+}
+
Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type)
{
if (type < texlist_count)
@@ -40,7 +57,7 @@ TexAtlas *asset_getTextureAtlas(AssetManager *assetManager, const enum TexList t
return NULL;
}
-Animation *asset_getAnim(AssetManager *assetManager, i32 type)
+Animation *asset_getAnim(AssetManager *assetManager, const enum AnimList type)
{
if (type < animlist_count)
return &assetManager->anims[type];
@@ -52,6 +69,35 @@ Animation *asset_getAnim(AssetManager *assetManager, i32 type)
return NULL;
}
+const i32 asset_loadVorbis(AssetManager *assetManager, MemoryArena *arena,
+ const char *const path, const enum AudioList type)
+{
+ // TODO(doyle): Remember to free vorbis file if we remove from memory
+ PlatformFileRead fileRead = {0};
+ platform_readFileToBuffer(arena, path, &fileRead);
+
+ i32 error;
+ AudioVorbis audio = {0};
+ audio.file =
+ stb_vorbis_open_memory(fileRead.buffer, fileRead.size, &error, NULL);
+
+ if (!audio.file)
+ {
+ printf("stb_vorbis_open_memory() failed: Error code %d\n", error);
+ platform_closeFileRead(arena, &fileRead);
+ stb_vorbis_close(audio.file);
+ return 0;
+ }
+
+ audio.info = stb_vorbis_get_info(audio.file);
+ audio.lengthInSamples = stb_vorbis_stream_length_in_samples(audio.file);
+ audio.lengthInSeconds = stb_vorbis_stream_length_in_seconds(audio.file);
+
+ assetManager->audio[type] = audio;
+
+ return 0;
+}
+
const i32 asset_loadTextureImage(AssetManager *assetManager,
const char *const path, const enum TexList type)
{
diff --git a/src/Audio.c b/src/Audio.c
new file mode 100644
index 0000000..99ce6ac
--- /dev/null
+++ b/src/Audio.c
@@ -0,0 +1,170 @@
+#include
+
+#define STB_VORBIS_HEADER_ONLY
+#include
+
+#include "Dengine/Assets.h"
+#include "Dengine/Audio.h"
+#include "Dengine/Common.h"
+#include "Dengine/Debug.h"
+
+#define AL_CHECK_ERROR() alCheckError_(__FILE__, __LINE__);
+void alCheckError_(const char *file, int line)
+{
+
+ ALenum errorCode;
+ while ((errorCode = alGetError()) != AL_NO_ERROR)
+ {
+ printf("OPENAL ");
+ switch(errorCode)
+ {
+ case AL_INVALID_NAME:
+ printf("INVALID_NAME | ");
+ break;
+ case AL_INVALID_ENUM:
+ printf("INVALID_ENUM | ");
+ break;
+ case AL_INVALID_VALUE:
+ printf("INVALID_VALUE | ");
+ break;
+ case AL_INVALID_OPERATION:
+ printf("INVALID_OPERATION | ");
+ break;
+ case AL_OUT_OF_MEMORY:
+ printf("OUT_OF_MEMORY | ");
+ break;
+ default:
+ printf("UNRECOGNISED ERROR CODE | ");
+ break;
+ }
+ printf("Error %08x, %s (%d)\n", errorCode, file, line);
+ }
+};
+
+const i32 audio_rendererInit(AudioRenderer *audioRenderer)
+{
+ alGetError();
+ ALCdevice *deviceAL = alcOpenDevice(NULL);
+ if (!deviceAL)
+ {
+ printf("alcOpenDevice() failed: Failed to init OpenAL device.\n");
+ return -1;
+ }
+
+ ALCcontext *contextAL = alcCreateContext(deviceAL, NULL);
+ alcMakeContextCurrent(contextAL);
+ if (!contextAL)
+ {
+ printf("alcCreateContext() failed: Failed create AL context.\n");
+ return -1;
+ }
+ AL_CHECK_ERROR();
+
+ /* Generate number of concurrent audio file listeners */
+ alGenSources(ARRAY_COUNT(audioRenderer->sourceId), audioRenderer->sourceId);
+ AL_CHECK_ERROR();
+
+ /* Generate audio data buffers */
+ alGenBuffers(ARRAY_COUNT(audioRenderer->bufferId), audioRenderer->bufferId);
+ AL_CHECK_ERROR();
+
+ return 0;
+}
+
+#define AUDIO_CHUNK_SIZE_ 65536
+void audio_streamVorbis(AudioRenderer *audioRenderer, AudioVorbis *vorbis)
+{
+ /* Determine format */
+ audioRenderer->format = AL_FORMAT_MONO16;
+ if (vorbis->info.channels == 2)
+ audioRenderer->format = AL_FORMAT_STEREO16;
+ else if (vorbis->info.channels != 1)
+ DEBUG_LOG("audio_streamVorbis() warning: Unaccounted channel format");
+
+ audioRenderer->audio = vorbis;
+ AudioVorbis *audio = audioRenderer->audio;
+
+ /* Pre-load vorbis data into audio chunks */
+ for (i32 i = 0; i < ARRAY_COUNT(audioRenderer->bufferId); i++)
+ {
+ i16 audioChunk[AUDIO_CHUNK_SIZE_] = {0};
+ stb_vorbis_get_samples_short_interleaved(
+ audio->file, audio->info.channels, audioChunk, AUDIO_CHUNK_SIZE_);
+
+ alBufferData(audioRenderer->bufferId[i], audioRenderer->format,
+ audioChunk, AUDIO_CHUNK_SIZE_ * sizeof(i16),
+ audio->info.sample_rate);
+ }
+
+ /* Queue and play buffers */
+ alSourceQueueBuffers(audioRenderer->sourceId[0],
+ ARRAY_COUNT(audioRenderer->bufferId),
+ audioRenderer->bufferId);
+ alSourcePlay(audioRenderer->sourceId[0]);
+}
+
+void audio_updateAndPlay(AudioRenderer *audioRenderer)
+{
+ AudioVorbis *audio = audioRenderer->audio;
+
+ ALint audioState;
+ alGetSourcei(audioRenderer->sourceId[0], AL_SOURCE_STATE, &audioState);
+ if (audioState == AL_STOPPED || audioState == AL_INITIAL)
+ {
+ // TODO(doyle): This fixes clicking when reusing old buffers
+ if (audioState == AL_STOPPED)
+ {
+ alDeleteBuffers(ARRAY_COUNT(audioRenderer->bufferId),
+ audioRenderer->bufferId);
+ alGenBuffers(ARRAY_COUNT(audioRenderer->bufferId),
+ audioRenderer->bufferId);
+ }
+
+ stb_vorbis_seek_start(audio->file);
+ for (i32 i = 0; i < ARRAY_COUNT(audioRenderer->bufferId); i++)
+ {
+ i16 audioChunk[AUDIO_CHUNK_SIZE_] = {0};
+ stb_vorbis_get_samples_short_interleaved(
+ audio->file, audio->info.channels, audioChunk,
+ AUDIO_CHUNK_SIZE_);
+
+ alBufferData(audioRenderer->bufferId[i], audioRenderer->format,
+ audioChunk, AUDIO_CHUNK_SIZE_ * sizeof(i16),
+ audio->info.sample_rate);
+ }
+
+ alSourceQueueBuffers(audioRenderer->sourceId[0],
+ ARRAY_COUNT(audioRenderer->bufferId),
+ audioRenderer->bufferId);
+ alSourcePlay(audioRenderer->sourceId[0]);
+ }
+ else if (audioState == AL_PLAYING)
+ {
+ ALint numProcessedBuffers;
+ alGetSourcei(audioRenderer->sourceId[0], AL_BUFFERS_PROCESSED,
+ &numProcessedBuffers);
+ if (numProcessedBuffers > 0)
+ {
+ ALint numBuffersToUnqueue = 1;
+ ALuint emptyBufferId;
+ alSourceUnqueueBuffers(audioRenderer->sourceId[0],
+ numBuffersToUnqueue, &emptyBufferId);
+
+ i16 audioChunk[AUDIO_CHUNK_SIZE_] = {0};
+ i32 sampleCount = stb_vorbis_get_samples_short_interleaved(
+ audio->file, audio->info.channels, audioChunk,
+ AUDIO_CHUNK_SIZE_);
+
+ /* There are still samples to play */
+ if (sampleCount > 0)
+ {
+ DEBUG_LOG("Buffering new audio data");
+ alBufferData(emptyBufferId, audioRenderer->format, audioChunk,
+ sampleCount * audio->info.channels * sizeof(i16),
+ audio->info.sample_rate);
+ alSourceQueueBuffers(audioRenderer->sourceId[0], 1,
+ &emptyBufferId);
+ }
+ }
+ }
+}
diff --git a/src/dengine.c b/src/dengine.c
index 543e31c..b2265c7 100644
--- a/src/dengine.c
+++ b/src/dengine.c
@@ -1,11 +1,6 @@
#if 1
-#include
-#include
-
-#define _CRT_SECURE_NO_WARNINGS
-#include
-
#include "Dengine/AssetManager.h"
+#include "Dengine/Audio.h"
#include "Dengine/Common.h"
#include "Dengine/Debug.h"
#include "Dengine/Math.h"
@@ -14,38 +9,8 @@
#include "WorldTraveller/WorldTraveller.h"
-void alCheckError_(const char *file, int line)
-{
-
- ALenum errorCode;
- while ((errorCode = alGetError()) != AL_NO_ERROR)
- {
- printf("OPENAL ");
- switch(errorCode)
- {
- case AL_INVALID_NAME:
- printf("INVALID_NAME | ");
- break;
- case AL_INVALID_ENUM:
- printf("INVALID_ENUM | ");
- break;
- case AL_INVALID_VALUE:
- printf("INVALID_VALUE | ");
- break;
- case AL_INVALID_OPERATION:
- printf("INVALID_OPERATION | ");
- break;
- case AL_OUT_OF_MEMORY:
- printf("OUT_OF_MEMORY | ");
- break;
- default:
- printf("UNRECOGNISED ERROR CODE | ");
- break;
- }
- printf("Error %08x, %s (%d)\n", errorCode, file, line);
- }
-};
-#define AL_CHECK_ERROR() alCheckError_(__FILE__, __LINE__);
+// TODO(doyle): Temporary
+struct AudioRenderer;
void key_callback(GLFWwindow *window, int key, int scancode, int action, int mode)
{
@@ -148,64 +113,20 @@ int main()
* INITIALISE AUDIO
*******************
*/
- alGetError();
- // TODO(doyle): Read this
- // http://www.gamedev.net/page/resources/_/technical/game-programming/basic-openal-sound-manager-for-your-project-r3791
- // https://gist.github.com/Oddity007/965399
- // https://jogamp.org/joal-demos/www/devmaster/lesson8.html
- // http://basic-converter.proboards.com/thread/818/play-files-using-vorbis-openal
- ALCdevice *deviceAL = alcOpenDevice(NULL);
- if (!deviceAL)
- {
- printf("alcOpenDevice() failed: Failed to init OpenAL device.\n");
- return;
- }
+ AudioRenderer audioRenderer = {0};
+ audio_rendererInit(&audioRenderer);
- ALCcontext *contextAL = alcCreateContext(deviceAL, NULL);
- alcMakeContextCurrent(contextAL);
- if (!contextAL)
- {
- printf("alcCreateContext() failed: Failed create AL context.\n");
- return;
- }
- AL_CHECK_ERROR();
+ /* Load audio assets */
+ char *audioPath = "data/audio/Nobuo Uematsu - Battle 1.ogg";
+ asset_loadVorbis(&worldTraveller.assetManager, &worldTraveller.arena,
+ audioPath, audiolist_battle);
+ audioPath = "data/audio/Yuki Kajiura - Swordland.ogg";
+ asset_loadVorbis(&worldTraveller.assetManager, &worldTraveller.arena,
+ audioPath, audiolist_overworld);
+ AudioVorbis *audio =
+ asset_getVorbis(&worldTraveller.assetManager, audiolist_battle);
- /* Open audio file */
- PlatformFileRead fileRead = {0};
-#if 0
- platform_readFileToBuffer(&worldTraveller.arena,
- "data/audio/Yuki Kajiura - Swordland.ogg",
- &fileRead);
-#else
- platform_readFileToBuffer(&worldTraveller.arena,
- "data/audio/Nobuo Uematsu - Battle 1.ogg",
- &fileRead);
-#endif
-
- i32 error;
- stb_vorbis *vorbisFile = stb_vorbis_open_memory(fileRead.buffer, fileRead.size,
- &error, NULL);
- stb_vorbis_info vorbisInfo = stb_vorbis_get_info(vorbisFile);
-
- //platform_closeFileRead(&worldTraveller.arena, &fileRead);
-
- /* Generate number of concurrent audio file listeners */
- ALuint audioSourceId;
- alGenSources(1, &audioSourceId);
- AL_CHECK_ERROR();
-
- /* Generate audio data buffers */
- ALuint audioBufferId[4];
- alGenBuffers(ARRAY_COUNT(audioBufferId), audioBufferId);
- AL_CHECK_ERROR();
-
-
-#if 0
- ALuint audioFormat = AL_FORMAT_MONO16;
- if (vorbisInfo.channels == 2) audioFormat = AL_FORMAT_STEREO16;
- i32 audioState;
- alGetSourcei(audioSourceIds[0], AL_SOURCE_STATE, &audioState);
-#endif
+ audio_streamVorbis(&audioRenderer, audio);
/*
*******************
@@ -237,6 +158,7 @@ int main()
worldTraveller_gameUpdateAndRender(&worldTraveller, secondsElapsed);
GL_CHECK_ERROR();
+ audio_updateAndPlay(&audioRenderer);
/* Swap the buffers */
glfwSwapBuffers(window);
@@ -244,64 +166,6 @@ int main()
f32 endTime = CAST(f32)glfwGetTime();
secondsElapsed = endTime - startTime;
-#define AUDIO_CHUNK_SIZE 65536
- ALint audioState;
- alGetSourcei(audioSourceId, AL_SOURCE_STATE, &audioState);
- if (audioState == AL_STOPPED || audioState == AL_INITIAL)
- {
- // TODO(doyle): This fixes clicking when reusing old buffers
- if (audioState == AL_STOPPED)
- {
- alDeleteBuffers(ARRAY_COUNT(audioBufferId), audioBufferId);
- alGenBuffers(ARRAY_COUNT(audioBufferId), audioBufferId);
- }
-
- stb_vorbis_seek_start(vorbisFile);
- for (i32 i = 0; i < ARRAY_COUNT(audioBufferId); i++)
- {
- i16 audioChunk[AUDIO_CHUNK_SIZE] = {0};
- stb_vorbis_get_samples_short_interleaved(
- vorbisFile, vorbisInfo.channels, audioChunk,
- AUDIO_CHUNK_SIZE);
-
- alBufferData(audioBufferId[i], AL_FORMAT_STEREO16, audioChunk,
- AUDIO_CHUNK_SIZE * sizeof(i16),
- vorbisInfo.sample_rate);
- }
-
- alSourceQueueBuffers(audioSourceId, ARRAY_COUNT(audioBufferId),
- audioBufferId);
- alSourcePlay(audioSourceId);
- }
- else if (audioState == AL_PLAYING)
- {
- ALint numProcessedBuffers;
- alGetSourcei(audioSourceId, AL_BUFFERS_PROCESSED,
- &numProcessedBuffers);
- if (numProcessedBuffers > 0)
- {
- ALint numBuffersToUnqueue = 1;
- ALuint emptyBufferId;
- alSourceUnqueueBuffers(audioSourceId, numBuffersToUnqueue,
- &emptyBufferId);
-
- i16 audioChunk[AUDIO_CHUNK_SIZE] = {0};
- i32 sampleCount = stb_vorbis_get_samples_short_interleaved(
- vorbisFile, vorbisInfo.channels, audioChunk,
- AUDIO_CHUNK_SIZE);
-
- /* There are still samples to play */
- if (sampleCount > 0)
- {
- DEBUG_LOG("Buffering new audio data");
- alBufferData(emptyBufferId, AL_FORMAT_STEREO16, audioChunk,
- sampleCount * vorbisInfo.channels *
- sizeof(i16),
- vorbisInfo.sample_rate);
- alSourceQueueBuffers(audioSourceId, 1, &emptyBufferId);
- }
- }
- }
#if 0
// TODO(doyle): Busy waiting, should sleep
while (secondsElapsed < targetSecondsPerFrame)
diff --git a/src/include/Dengine/AssetManager.h b/src/include/Dengine/AssetManager.h
index 0f0be67..040d7b6 100644
--- a/src/include/Dengine/AssetManager.h
+++ b/src/include/Dengine/AssetManager.h
@@ -2,30 +2,38 @@
#define DENGINE_ASSET_MANAGER_H
#include "Dengine/Assets.h"
-#include "Dengine/MemoryArena.h"
#include "Dengine/Shader.h"
#include "Dengine/Texture.h"
+/* Forward declaration */
+typedef struct MemoryArena MemoryArena;
+
#define MAX_TEXTURE_SIZE 1024
// TODO(doyle): Switch to hash based lookup
+// TODO(doyle): Use pointers, so we can forward declare all assets?
typedef struct AssetManager
{
Texture textures[32];
TexAtlas texAtlas[32];
Shader shaders[32];
Animation anims[32];
+ AudioVorbis audio[32];
Font font;
} AssetManager;
GLOBAL_VAR AssetManager assetManager;
+AudioVorbis *asset_getVorbis(AssetManager *assetManager,
+ const enum AudioList type);
Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type);
Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type);
TexAtlas *asset_getTextureAtlas(AssetManager *assetManager,
const enum TexList type);
Animation *asset_getAnim(AssetManager *assetManager, i32 type);
+const i32 asset_loadVorbis(AssetManager *assetManager, MemoryArena *arena,
+ const char *const path, const enum AudioList type);
const i32 asset_loadTextureImage(AssetManager *assetManager,
const char *const path,
const enum TexList type);
diff --git a/src/include/Dengine/Assets.h b/src/include/Dengine/Assets.h
index 2d3392b..841bdf4 100644
--- a/src/include/Dengine/Assets.h
+++ b/src/include/Dengine/Assets.h
@@ -1,6 +1,9 @@
#ifndef DENGINE_ASSETS_H
#define DENGINE_ASSETS_H
+#define STB_VORBIS_HEADER_ONLY
+#include
+
#include "Dengine/Math.h"
#include "Dengine/Texture.h"
@@ -52,6 +55,23 @@ enum AnimList
animlist_invalid,
};
+enum AudioList
+{
+ audiolist_battle,
+ audiolist_overworld,
+ audiolist_count,
+ audiolist_invalid,
+};
+
+typedef struct AudioVorbis
+{
+ stb_vorbis *file;
+ stb_vorbis_info info;
+
+ u32 lengthInSamples;
+ f32 lengthInSeconds;
+} AudioVorbis;
+
typedef struct TexAtlas
{
// TODO(doyle): String hash based lookup
diff --git a/src/include/Dengine/Audio.h b/src/include/Dengine/Audio.h
new file mode 100644
index 0000000..e172f5f
--- /dev/null
+++ b/src/include/Dengine/Audio.h
@@ -0,0 +1,18 @@
+#ifndef DENGINE_AUDIO_H
+#define DENGINE_AUDIO_H
+
+#include
+
+typedef struct AudioRenderer
+{
+ ALuint sourceId[1];
+ ALuint bufferId[4];
+
+ AudioVorbis *audio;
+ ALuint format;
+} AudioRenderer;
+
+const i32 audio_rendererInit(AudioRenderer *audioRenderer);
+void audio_streamVorbis(AudioRenderer *audioRenderer, AudioVorbis *vorbis);
+void audio_updateAndPlay(AudioRenderer *audioRenderer);
+#endif
diff --git a/src/include/Dengine/MemoryArena.h b/src/include/Dengine/MemoryArena.h
index ebf2618..255249b 100644
--- a/src/include/Dengine/MemoryArena.h
+++ b/src/include/Dengine/MemoryArena.h
@@ -1,9 +1,11 @@
#ifndef DENGINE_MEMORY_ARENA_H
#define DENGINE_MEMORY_ARENA_H
-typedef struct MemoryArena
+#include "Dengine/Common.h"
+
+struct MemoryArena
{
i32 bytesAllocated;
-} MemoryArena;
+};
#endif
diff --git a/src/include/Dengine/Platform.h b/src/include/Dengine/Platform.h
index f1afdf7..23b036a 100644
--- a/src/include/Dengine/Platform.h
+++ b/src/include/Dengine/Platform.h
@@ -2,7 +2,9 @@
#define DENGINE_PLATFORM_H
#include "Dengine/Common.h"
-#include "Dengine/MemoryArena.h"
+
+/* Forward Declaration */
+typedef struct MemoryArena MemoryArena;
typedef struct PlatformFileRead
{
diff --git a/src/include/WorldTraveller/WorldTraveller.h b/src/include/WorldTraveller/WorldTraveller.h
index 0ea6723..a74a36c 100644
--- a/src/include/WorldTraveller/WorldTraveller.h
+++ b/src/include/WorldTraveller/WorldTraveller.h
@@ -8,6 +8,9 @@
#include "Dengine/MemoryArena.h"
#include "Dengine/Renderer.h"
+/* Forward Declaration */
+typedef struct MemoryArena MemoryArena;
+
#define NUM_KEYS 1024
#define METERS_TO_PIXEL 240