Abstract audio playback into own file
This commit is contained in:
parent
99a700ca13
commit
84a0f755ea
@ -121,6 +121,7 @@
|
|||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="src\AssetManager.c" />
|
<ClCompile Include="src\AssetManager.c" />
|
||||||
|
<ClCompile Include="src\Audio.c" />
|
||||||
<ClCompile Include="src\Common.c" />
|
<ClCompile Include="src\Common.c" />
|
||||||
<ClCompile Include="src\Debug.c" />
|
<ClCompile Include="src\Debug.c" />
|
||||||
<ClCompile Include="src\dengine.c" />
|
<ClCompile Include="src\dengine.c" />
|
||||||
@ -139,6 +140,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="src\include\Dengine\Assets.h" />
|
<ClInclude Include="src\include\Dengine\Assets.h" />
|
||||||
|
<ClInclude Include="src\include\Dengine\Audio.h" />
|
||||||
<ClInclude Include="src\include\Dengine\Debug.h" />
|
<ClInclude Include="src\include\Dengine\Debug.h" />
|
||||||
<ClInclude Include="src\include\Dengine\MemoryArena.h" />
|
<ClInclude Include="src\include\Dengine\MemoryArena.h" />
|
||||||
<ClInclude Include="src\include\Dengine\Platform.h" />
|
<ClInclude Include="src\include\Dengine\Platform.h" />
|
||||||
|
@ -45,6 +45,9 @@
|
|||||||
<ClCompile Include="src\Debug.c">
|
<ClCompile Include="src\Debug.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="src\Audio.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="data\shaders\default.vert.glsl" />
|
<None Include="data\shaders\default.vert.glsl" />
|
||||||
@ -95,5 +98,8 @@
|
|||||||
<ClInclude Include="src\include\Dengine\MemoryArena.h">
|
<ClInclude Include="src\include\Dengine\MemoryArena.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\include\Dengine\Audio.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -1,3 +1,5 @@
|
|||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
|
||||||
#define STBI_FAILURE_USERMSG
|
#define STBI_FAILURE_USERMSG
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include <STB/stb_image.h>
|
#include <STB/stb_image.h>
|
||||||
@ -6,17 +8,32 @@
|
|||||||
#define STB_TRUETYPE_IMPLEMENTATION
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
#include <STB/stb_truetype.h>
|
#include <STB/stb_truetype.h>
|
||||||
|
|
||||||
#include "Dengine/AssetManager.h"
|
|
||||||
#include "Dengine/Debug.h"
|
|
||||||
#include "Dengine/OpenGL.h"
|
|
||||||
#include "Dengine/Platform.h"
|
|
||||||
|
|
||||||
//#define WT_RENDER_FONT_FILE
|
//#define WT_RENDER_FONT_FILE
|
||||||
#ifdef WT_RENDER_FONT_FILE
|
#ifdef WT_RENDER_FONT_FILE
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#include <STB/stb_image_write.h>
|
#include <STB/stb_image_write.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <STB/stb_vorbis.c>
|
||||||
|
|
||||||
|
#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)
|
Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type)
|
||||||
{
|
{
|
||||||
if (type < texlist_count)
|
if (type < texlist_count)
|
||||||
@ -40,7 +57,7 @@ TexAtlas *asset_getTextureAtlas(AssetManager *assetManager, const enum TexList t
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Animation *asset_getAnim(AssetManager *assetManager, i32 type)
|
Animation *asset_getAnim(AssetManager *assetManager, const enum AnimList type)
|
||||||
{
|
{
|
||||||
if (type < animlist_count)
|
if (type < animlist_count)
|
||||||
return &assetManager->anims[type];
|
return &assetManager->anims[type];
|
||||||
@ -52,6 +69,35 @@ Animation *asset_getAnim(AssetManager *assetManager, i32 type)
|
|||||||
return NULL;
|
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 i32 asset_loadTextureImage(AssetManager *assetManager,
|
||||||
const char *const path, const enum TexList type)
|
const char *const path, const enum TexList type)
|
||||||
{
|
{
|
||||||
|
170
src/Audio.c
Normal file
170
src/Audio.c
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
#include <OpenAL/alc.h>
|
||||||
|
|
||||||
|
#define STB_VORBIS_HEADER_ONLY
|
||||||
|
#include <STB/stb_vorbis.c>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
168
src/dengine.c
168
src/dengine.c
@ -1,11 +1,6 @@
|
|||||||
#if 1
|
#if 1
|
||||||
#include <OpenAL/al.h>
|
|
||||||
#include <OpenAL/alc.h>
|
|
||||||
|
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
|
||||||
#include <STB/stb_vorbis.c>
|
|
||||||
|
|
||||||
#include "Dengine/AssetManager.h"
|
#include "Dengine/AssetManager.h"
|
||||||
|
#include "Dengine/Audio.h"
|
||||||
#include "Dengine/Common.h"
|
#include "Dengine/Common.h"
|
||||||
#include "Dengine/Debug.h"
|
#include "Dengine/Debug.h"
|
||||||
#include "Dengine/Math.h"
|
#include "Dengine/Math.h"
|
||||||
@ -14,38 +9,8 @@
|
|||||||
|
|
||||||
#include "WorldTraveller/WorldTraveller.h"
|
#include "WorldTraveller/WorldTraveller.h"
|
||||||
|
|
||||||
void alCheckError_(const char *file, int line)
|
// TODO(doyle): Temporary
|
||||||
{
|
struct AudioRenderer;
|
||||||
|
|
||||||
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__);
|
|
||||||
|
|
||||||
void key_callback(GLFWwindow *window, int key, int scancode, int action, int mode)
|
void key_callback(GLFWwindow *window, int key, int scancode, int action, int mode)
|
||||||
{
|
{
|
||||||
@ -148,64 +113,20 @@ int main()
|
|||||||
* INITIALISE AUDIO
|
* INITIALISE AUDIO
|
||||||
*******************
|
*******************
|
||||||
*/
|
*/
|
||||||
alGetError();
|
AudioRenderer audioRenderer = {0};
|
||||||
// TODO(doyle): Read this
|
audio_rendererInit(&audioRenderer);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
ALCcontext *contextAL = alcCreateContext(deviceAL, NULL);
|
/* Load audio assets */
|
||||||
alcMakeContextCurrent(contextAL);
|
char *audioPath = "data/audio/Nobuo Uematsu - Battle 1.ogg";
|
||||||
if (!contextAL)
|
asset_loadVorbis(&worldTraveller.assetManager, &worldTraveller.arena,
|
||||||
{
|
audioPath, audiolist_battle);
|
||||||
printf("alcCreateContext() failed: Failed create AL context.\n");
|
audioPath = "data/audio/Yuki Kajiura - Swordland.ogg";
|
||||||
return;
|
asset_loadVorbis(&worldTraveller.assetManager, &worldTraveller.arena,
|
||||||
}
|
audioPath, audiolist_overworld);
|
||||||
AL_CHECK_ERROR();
|
AudioVorbis *audio =
|
||||||
|
asset_getVorbis(&worldTraveller.assetManager, audiolist_battle);
|
||||||
|
|
||||||
/* Open audio file */
|
audio_streamVorbis(&audioRenderer, audio);
|
||||||
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
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*******************
|
*******************
|
||||||
@ -237,6 +158,7 @@ int main()
|
|||||||
|
|
||||||
worldTraveller_gameUpdateAndRender(&worldTraveller, secondsElapsed);
|
worldTraveller_gameUpdateAndRender(&worldTraveller, secondsElapsed);
|
||||||
GL_CHECK_ERROR();
|
GL_CHECK_ERROR();
|
||||||
|
audio_updateAndPlay(&audioRenderer);
|
||||||
|
|
||||||
/* Swap the buffers */
|
/* Swap the buffers */
|
||||||
glfwSwapBuffers(window);
|
glfwSwapBuffers(window);
|
||||||
@ -244,64 +166,6 @@ int main()
|
|||||||
f32 endTime = CAST(f32)glfwGetTime();
|
f32 endTime = CAST(f32)glfwGetTime();
|
||||||
secondsElapsed = endTime - startTime;
|
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
|
#if 0
|
||||||
// TODO(doyle): Busy waiting, should sleep
|
// TODO(doyle): Busy waiting, should sleep
|
||||||
while (secondsElapsed < targetSecondsPerFrame)
|
while (secondsElapsed < targetSecondsPerFrame)
|
||||||
|
@ -2,30 +2,38 @@
|
|||||||
#define DENGINE_ASSET_MANAGER_H
|
#define DENGINE_ASSET_MANAGER_H
|
||||||
|
|
||||||
#include "Dengine/Assets.h"
|
#include "Dengine/Assets.h"
|
||||||
#include "Dengine/MemoryArena.h"
|
|
||||||
#include "Dengine/Shader.h"
|
#include "Dengine/Shader.h"
|
||||||
#include "Dengine/Texture.h"
|
#include "Dengine/Texture.h"
|
||||||
|
|
||||||
|
/* Forward declaration */
|
||||||
|
typedef struct MemoryArena MemoryArena;
|
||||||
|
|
||||||
#define MAX_TEXTURE_SIZE 1024
|
#define MAX_TEXTURE_SIZE 1024
|
||||||
|
|
||||||
// TODO(doyle): Switch to hash based lookup
|
// TODO(doyle): Switch to hash based lookup
|
||||||
|
// TODO(doyle): Use pointers, so we can forward declare all assets?
|
||||||
typedef struct AssetManager
|
typedef struct AssetManager
|
||||||
{
|
{
|
||||||
Texture textures[32];
|
Texture textures[32];
|
||||||
TexAtlas texAtlas[32];
|
TexAtlas texAtlas[32];
|
||||||
Shader shaders[32];
|
Shader shaders[32];
|
||||||
Animation anims[32];
|
Animation anims[32];
|
||||||
|
AudioVorbis audio[32];
|
||||||
Font font;
|
Font font;
|
||||||
} AssetManager;
|
} AssetManager;
|
||||||
|
|
||||||
GLOBAL_VAR AssetManager assetManager;
|
GLOBAL_VAR AssetManager assetManager;
|
||||||
|
|
||||||
|
AudioVorbis *asset_getVorbis(AssetManager *assetManager,
|
||||||
|
const enum AudioList type);
|
||||||
Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type);
|
Texture *asset_getTexture(AssetManager *assetManager, const enum TexList type);
|
||||||
Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type);
|
Shader *asset_getShader(AssetManager *assetManager, const enum ShaderList type);
|
||||||
TexAtlas *asset_getTextureAtlas(AssetManager *assetManager,
|
TexAtlas *asset_getTextureAtlas(AssetManager *assetManager,
|
||||||
const enum TexList type);
|
const enum TexList type);
|
||||||
Animation *asset_getAnim(AssetManager *assetManager, i32 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 i32 asset_loadTextureImage(AssetManager *assetManager,
|
||||||
const char *const path,
|
const char *const path,
|
||||||
const enum TexList type);
|
const enum TexList type);
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
#ifndef DENGINE_ASSETS_H
|
#ifndef DENGINE_ASSETS_H
|
||||||
#define DENGINE_ASSETS_H
|
#define DENGINE_ASSETS_H
|
||||||
|
|
||||||
|
#define STB_VORBIS_HEADER_ONLY
|
||||||
|
#include <STB/stb_vorbis.c>
|
||||||
|
|
||||||
#include "Dengine/Math.h"
|
#include "Dengine/Math.h"
|
||||||
#include "Dengine/Texture.h"
|
#include "Dengine/Texture.h"
|
||||||
|
|
||||||
@ -52,6 +55,23 @@ enum AnimList
|
|||||||
animlist_invalid,
|
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
|
typedef struct TexAtlas
|
||||||
{
|
{
|
||||||
// TODO(doyle): String hash based lookup
|
// TODO(doyle): String hash based lookup
|
||||||
|
18
src/include/Dengine/Audio.h
Normal file
18
src/include/Dengine/Audio.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef DENGINE_AUDIO_H
|
||||||
|
#define DENGINE_AUDIO_H
|
||||||
|
|
||||||
|
#include <OpenAL/al.h>
|
||||||
|
|
||||||
|
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
|
@ -1,9 +1,11 @@
|
|||||||
#ifndef DENGINE_MEMORY_ARENA_H
|
#ifndef DENGINE_MEMORY_ARENA_H
|
||||||
#define DENGINE_MEMORY_ARENA_H
|
#define DENGINE_MEMORY_ARENA_H
|
||||||
|
|
||||||
typedef struct MemoryArena
|
#include "Dengine/Common.h"
|
||||||
|
|
||||||
|
struct MemoryArena
|
||||||
{
|
{
|
||||||
i32 bytesAllocated;
|
i32 bytesAllocated;
|
||||||
} MemoryArena;
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
#define DENGINE_PLATFORM_H
|
#define DENGINE_PLATFORM_H
|
||||||
|
|
||||||
#include "Dengine/Common.h"
|
#include "Dengine/Common.h"
|
||||||
#include "Dengine/MemoryArena.h"
|
|
||||||
|
/* Forward Declaration */
|
||||||
|
typedef struct MemoryArena MemoryArena;
|
||||||
|
|
||||||
typedef struct PlatformFileRead
|
typedef struct PlatformFileRead
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
#include "Dengine/MemoryArena.h"
|
#include "Dengine/MemoryArena.h"
|
||||||
#include "Dengine/Renderer.h"
|
#include "Dengine/Renderer.h"
|
||||||
|
|
||||||
|
/* Forward Declaration */
|
||||||
|
typedef struct MemoryArena MemoryArena;
|
||||||
|
|
||||||
#define NUM_KEYS 1024
|
#define NUM_KEYS 1024
|
||||||
#define METERS_TO_PIXEL 240
|
#define METERS_TO_PIXEL 240
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user