diff --git a/client/main.c b/client/main.c index ed8d512..cd560a3 100644 --- a/client/main.c +++ b/client/main.c @@ -69,14 +69,23 @@ int main(int argc, char** argv){ GSInit(); if((engine = GSEngineCreate(¶m)) != NULL){ + GSSound sound; + signal(SIGINT, shutdown_engine); signal(SIGTERM, shutdown_engine); model = GSModelOpen(engine, "game:/mdl/fish.gsm"); GSClientToggleSkybox(GSEngineGetClient(engine), GSTrue); + + if((sound = GSSoundOpen(GSClientGetSoundEngine(GSEngineGetClient(engine)), "game:/fracture.mod")) != NULL){ + GSSoundToggleLoop(sound, 1); + GSSoundStart(sound); + } GSEngineLoop(engine); + + if(sound != NULL) GSSoundClose(sound); GSEngineDestroy(engine); } } diff --git a/engine/include/GearSrc/Client.h b/engine/include/GearSrc/Client.h index ae13f67..d09e4ef 100644 --- a/engine/include/GearSrc/Client.h +++ b/engine/include/GearSrc/Client.h @@ -8,11 +8,12 @@ extern "C" { #endif -GSDECL GSClient GSClientCreate(GSEngine engine); -GSDECL void GSClientDestroy(GSClient client); -GSDECL void GSClientStep(GSClient client); -GSDECL GSGL GSClientGetGL(GSClient client); -GSDECL void GSClientToggleSkybox(GSClient client, GSBool toggle); +GSDECL GSClient GSClientCreate(GSEngine engine); +GSDECL void GSClientDestroy(GSClient client); +GSDECL void GSClientStep(GSClient client); +GSDECL GSGL GSClientGetGL(GSClient client); +GSDECL GSSoundEngine GSClientGetSoundEngine(GSClient client); +GSDECL void GSClientToggleSkybox(GSClient client, GSBool toggle); #ifdef __cplusplus } diff --git a/engine/include/GearSrc/Foundation.h b/engine/include/GearSrc/Foundation.h index 00cd459..6549585 100644 --- a/engine/include/GearSrc/Foundation.h +++ b/engine/include/GearSrc/Foundation.h @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #endif diff --git a/engine/include/GearSrc/Sound.h b/engine/include/GearSrc/Sound.h index cd61733..6e7a39a 100644 --- a/engine/include/GearSrc/Sound.h +++ b/engine/include/GearSrc/Sound.h @@ -8,10 +8,22 @@ extern "C" { #endif -#define GSSoundRate 48000 - -GSDECL GSSound GSSoundOpen(GSClient client); +/* sound.c */ +GSDECL GSSound GSSoundNew(GSSoundEngine sengine); +GSDECL GSSound GSSoundOpen(GSSoundEngine sengine, const char* path); GSDECL void GSSoundClose(GSSound sound); +GSDECL void GSSoundToggleLoop(GSSound sound, GSBool toggle); +GSDECL void GSSoundStart(GSSound sound); +GSDECL void GSSoundResume(GSSound sound); +GSDECL void GSSoundPause(GSSound audio); +GSDECL void GSSoundLock(GSSound sound); +GSDECL void GSSoundUnlock(GSSound sound); + +/* snd_xm.c */ +GSDECL GSSound GSSoundOpenXM(GSSoundEngine sengine, GSFile file); + +/* snd_mod.c */ +GSDECL GSSound GSSoundOpenMOD(GSSoundEngine sengine, GSFile file); #ifdef __cplusplus } diff --git a/engine/include/GearSrc/SoundDriver.h b/engine/include/GearSrc/SoundDriver.h new file mode 100644 index 0000000..e2f963e --- /dev/null +++ b/engine/include/GearSrc/SoundDriver.h @@ -0,0 +1,20 @@ +#ifndef __GEARSRC_SOUNDDRIVER_H__ +#define __GEARSRC_SOUNDDRIVER_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSSoundDriverRate 48000 + +GSDECL GSSoundDriver GSSoundDriverOpen(GSClient client, GSSoundDriverReadCallback callback, void* opaque); +GSDECL void GSSoundDriverClose(GSSoundDriver sound); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/engine/include/GearSrc/SoundEngine.h b/engine/include/GearSrc/SoundEngine.h new file mode 100644 index 0000000..02b8c60 --- /dev/null +++ b/engine/include/GearSrc/SoundEngine.h @@ -0,0 +1,20 @@ +#ifndef __GEARSRC_SOUNDENGINE_H__ +#define __GEARSRC_SOUNDENGINE_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +GSDECL GSSoundEngine GSSoundEngineCreate(GSClient client); +GSDECL void GSSoundEngineDestroy(GSSoundEngine sengine); +GSDECL void GSSoundEngineLock(GSSoundEngine sengine); +GSDECL void GSSoundEngineUnlock(GSSoundEngine sengine); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/engine/include/GearSrc/TypeDefs.h b/engine/include/GearSrc/TypeDefs.h index ee2de57..dea99a3 100644 --- a/engine/include/GearSrc/TypeDefs.h +++ b/engine/include/GearSrc/TypeDefs.h @@ -46,15 +46,17 @@ typedef struct _GSEngineParam GSEngineParam; typedef struct _GSResourceKV GSResourceKV; typedef struct _GSBox GSBox; #ifdef _GEARSRC -typedef struct _GSClient* GSClient; -typedef struct _GSServer* GSServer; -typedef struct _GSEngine* GSEngine; -typedef struct _GSFile* GSFile; -typedef struct _GSResource* GSResource; -typedef struct _GSGL* GSGL; -typedef struct _GSSkyBox* GSSkyBox; -typedef struct _GSModel* GSModel; -typedef struct _GSSound* GSSound; +typedef struct _GSClient* GSClient; +typedef struct _GSServer* GSServer; +typedef struct _GSEngine* GSEngine; +typedef struct _GSFile* GSFile; +typedef struct _GSResource* GSResource; +typedef struct _GSGL* GSGL; +typedef struct _GSSkyBox* GSSkyBox; +typedef struct _GSModel* GSModel; +typedef struct _GSSoundDriver* GSSoundDriver; +typedef struct _GSSoundEngine* GSSoundEngine; +typedef struct _GSSound* GSSound; typedef struct _GSModelFace GSModelFace; typedef struct _GSModelMaterial GSModelMaterial; @@ -69,6 +71,8 @@ typedef void* GSResource; typedef void* GSGL; typedef void* GSSkyBox; typedef void* GSModel; +typedef void* GSSoundDriver; +typedef void* GSSoundEngine; typedef void* GSSound; #endif typedef float GSNumber; @@ -85,6 +89,12 @@ typedef void (*GSSleepCallback)(int ms); typedef void (*GSRenderCallback)(GSEngine engine); typedef void (*GSAfterRenderCallback)(GSEngine engine); +typedef void (*GSSoundDriverReadCallback)(void* opaque, GSI16* out, int frame); + +typedef void (*GSSoundResetCallback)(GSSound audio); +typedef int (*GSSoundReadCallback)(GSSound audio, GSI16* out, int frame); +typedef void (*GSSoundCloseCallback)(GSSound audio); + struct _GSBox { GSVector3 a; GSVector3 b; @@ -162,8 +172,41 @@ struct _GSModel { struct _GSSound { GSEngine engine; + GSBool paused; + GSBool loop; + + ma_mutex mutex; + + GSFile file; + + void* opaque1; + void* opaque2; + + GSSoundResetCallback reset; + GSSoundReadCallback read; + GSSoundCloseCallback close; +}; + +struct _GSSoundDriver { + GSEngine engine; + ma_device device; ma_device_config config; + + int ready; + + GSSoundDriverReadCallback callback; + void* opaque; +}; + +struct _GSSoundEngine { + GSEngine engine; + + GSSoundDriver driver; + + ma_mutex mutex; + + GSSound* sound; }; struct _GSClient { @@ -189,7 +232,7 @@ struct _GSClient { GSSkyBox skybox; GSBool skybox_enabled; - GSSound sound; + GSSoundEngine sengine; }; struct _GSServer { diff --git a/engine/src/audio.c b/engine/src/audio.c deleted file mode 100644 index e69de29..0000000 diff --git a/engine/src/client.c b/engine/src/client.c index 04910a9..dd2b4d2 100644 --- a/engine/src/client.c +++ b/engine/src/client.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include @@ -50,9 +50,9 @@ GSClient GSClientCreate(GSEngine engine) { client->skybox_enabled = GSFalse; - if((client->sound = GSSoundOpen(client)) == NULL) { + if((client->sengine = GSSoundEngineCreate(client)) == NULL) { + GSLog(engine, GSLogError, "Failed to create sound engine"); GSClientDestroy(client); - GSLog(engine, GSLogError, "Failed to create client"); return NULL; } @@ -89,7 +89,7 @@ void GSClientDestroy(GSClient client) { GSGLDestroy(client->gl); - if(client->sound != NULL) GSSoundClose(client->sound); + if(client->sengine != NULL) GSSoundEngineDestroy(client->sengine); GSLog(client->engine, GSLogInfo, "Destroyed client"); @@ -252,6 +252,10 @@ GSGL GSClientGetGL(GSClient client) { return client->gl; } +GSSoundEngine GSClientGetSoundEngine(GSClient client) { + return client->sengine; +} + void GSClientToggleSkybox(GSClient client, GSBool toggle) { client->skybox_enabled = toggle; } diff --git a/engine/src/model.c b/engine/src/model.c index 624b1a2..bd5ee3c 100644 --- a/engine/src/model.c +++ b/engine/src/model.c @@ -270,7 +270,7 @@ GSModel GSModelOpen(GSEngine engine, const char* path) { GSEngineUnregisterResource(engine, "model-ws"); - GSLog(engine, GSLogInfo, "Opened model %s, %d vertexes, %d faces", path, arrlen(model->vertex), arrlen(model->face)); + GSLog(engine, GSLogInfo, "%s: %d vertexes, %d faces", path, arrlen(model->vertex), arrlen(model->face)); /* TODO: check for animation */ if(gl != NULL) { diff --git a/engine/src/snd_mod.c b/engine/src/snd_mod.c new file mode 100644 index 0000000..f5c8b78 --- /dev/null +++ b/engine/src/snd_mod.c @@ -0,0 +1,74 @@ +#include + +#include +#include + +#include + +static const char* sig[] = {"M!K!", "M.K.", "FLT4", "FLT8", "4CHN", "6CHN", "8CHN", "10CH", "12CH", "14CH", "16CH", "18CH", "20CH", "22CH", "24CH", "26CH", "28CH", "30CH", "32CH"}; + +static void snd_reset(GSSound sound) { + jar_mod_seek_start(sound->opaque1); +} + +static int snd_read(GSSound sound, GSI16* out, int frame) { + if(((jar_mod_context_t*)sound->opaque1)->loopcount > 0) return 0; + + jar_mod_fillbuffer(sound->opaque1, out, frame, NULL); + + return frame; +} + +static void snd_close(GSSound sound) { + if(sound->opaque1 != NULL) { + jar_mod_unload(sound->opaque1); + free(sound->opaque1); + } + free(sound->opaque2); +} + +static GSBool compare(void* buf) { + int i; + for(i = 0; i < sizeof(sig) / sizeof(sig[0]); i++) { + if(memcmp(((unsigned char*)buf) + 1080, sig[i], 4) == 0) return GSTrue; + } + + return GSFalse; +} + +GSSound GSSoundOpenMOD(GSSoundEngine sengine, GSFile file) { + GSSound sound = GSSoundNew(sengine); + int size; + + GSFileSeek(file, 0); + + size = GSFileSize(file); + sound->opaque2 = malloc(size); + GSFileRead(file, sound->opaque2, size); + + sound->reset = snd_reset; + sound->read = snd_read; + sound->close = snd_close; + + if(size < 1084 || !compare(sound->opaque2)) { + GSSoundClose(sound); + + return NULL; + } + + sound->opaque1 = malloc(sizeof(jar_mod_context_t)); + jar_mod_init(sound->opaque1); + + if(!jar_mod_load(sound->opaque1, sound->opaque2, size)) { + free(sound->opaque1); + sound->opaque1 = NULL; + + GSSoundClose(sound); + + return NULL; + } + + jar_mod_setcfg(sound->opaque1, GSSoundDriverRate, 16, 1, 1, 0); + + return sound; +} diff --git a/engine/src/snd_xm.c b/engine/src/snd_xm.c new file mode 100644 index 0000000..122b478 --- /dev/null +++ b/engine/src/snd_xm.c @@ -0,0 +1,52 @@ +#include + +#include +#include + +#include + +static void snd_reset(GSSound sound) { + jar_xm_reset(sound->opaque1); +} + +static int snd_read(GSSound sound, GSI16* out, int frame) { + if(jar_xm_get_loop_count(sound->opaque1) == 1) return 0; + + jar_xm_generate_samples_16bit(sound->opaque1, out, frame); + + return frame; +} + +static void snd_close(GSSound sound) { + if(sound->opaque1 != NULL) jar_xm_free_context(sound->opaque1); + free(sound->opaque2); +} + +GSSound GSSoundOpenXM(GSSoundEngine sengine, GSFile file) { + GSSound sound = GSSoundNew(sengine); + int size; + + GSFileSeek(file, 0); + + size = GSFileSize(file); + sound->opaque2 = malloc(size); + GSFileRead(file, sound->opaque2, size); + + sound->reset = snd_reset; + sound->read = snd_read; + sound->close = snd_close; + + if(size < 37 || memcmp(sound->opaque2, "Extended Module: ", 17) != 0 || ((unsigned char*)sound->opaque2)[37] != 0x1a) { + GSSoundClose(sound); + + return NULL; + } + + if(jar_xm_create_context_safe((jar_xm_context_t**)&sound->opaque1, sound->opaque2, size, GSSoundDriverRate) != 0) { + GSSoundClose(sound); + + return NULL; + } + + return sound; +} diff --git a/engine/src/sound.c b/engine/src/sound.c index 5625549..7e8bb12 100644 --- a/engine/src/sound.c +++ b/engine/src/sound.c @@ -1,48 +1,108 @@ #include +#include +#include +#include #include +#include #include -static void data_callback(ma_device* device, void* output, const void* input, ma_uint32 frame) { -} - -GSSound GSSoundOpen(GSClient client) { +GSSound GSSoundNew(GSSoundEngine sengine) { GSSound sound = malloc(sizeof(*sound)); memset(sound, 0, sizeof(*sound)); - sound->engine = client->engine; + sound->engine = sengine->engine; - sound->config = ma_device_config_init(ma_device_type_playback); - sound->config.playback.format = ma_format_s16; - sound->config.playback.channels = 2; - sound->config.sampleRate = GSSoundRate; - sound->config.dataCallback = data_callback; - sound->config.pUserData = sound; + sound->paused = 1; + sound->loop = 0; - if(ma_device_init(NULL, &sound->config, &sound->device) != MA_SUCCESS) { - free(sound); - GSLog(client->engine, GSLogInfo, "Failed to open sound"); - return NULL; - } + ma_mutex_init(&sound->mutex); - if(ma_device_start(&sound->device) != MA_SUCCESS) { - ma_device_uninit(&sound->device); - free(sound); - GSLog(client->engine, GSLogInfo, "Failed to open sound"); - return NULL; - } - - GSLog(client->engine, GSLogInfo, "Opened sound"); + GSSoundEngineLock(sengine); + arrput(sengine->sound, sound); + GSSoundEngineUnlock(sengine); return sound; } -void GSSoundClose(GSSound sound) { - ma_device_uninit(&sound->device); +GSSound GSSoundOpen(GSSoundEngine sengine, const char* path) { + GSSound s; + GSFile f; - GSLog(sound->engine, GSLogInfo, "Closed sound"); + if((f = GSFileOpen(sengine->engine, path)) == NULL) return NULL; + if((s = GSSoundOpenXM(sengine, f)) != NULL) { + s->file = f; + GSLog(sengine->engine, GSLogInfo, "%s: Opened as XM", path); + return s; + } + + if((s = GSSoundOpenMOD(sengine, f)) != NULL) { + s->file = f; + GSLog(sengine->engine, GSLogInfo, "%s: Opened as MOD", path); + return s; + } + + GSLog(sengine->engine, GSLogError, "%s: Unrecognized format", path); + + GSFileClose(f); + + return NULL; +} + +void GSSoundClose(GSSound sound) { + int i; + GSSoundEngine sengine = sound->engine->client->sengine; + + GSSoundEngineLock(sengine); + for(i = 0; i < arrlen(sengine->sound); i++) { + if(sengine->sound[i] == sound) { + arrdel(sengine->sound, i); + break; + } + } + GSSoundEngineUnlock(sengine); + + if(sound->close != NULL) sound->close(sound); + + ma_mutex_uninit(&sound->mutex); + + if(sound->file != NULL) GSFileClose(sound->file); free(sound); } + +void GSSoundToggleLoop(GSSound sound, GSBool toggle) { + GSSoundLock(sound); + sound->loop = toggle; + GSSoundUnlock(sound); +} + +void GSSoundStart(GSSound sound) { + GSSoundLock(sound); + if(sound->reset != NULL) sound->reset(sound); + GSSoundUnlock(sound); + + GSSoundResume(sound); +} + +void GSSoundResume(GSSound sound) { + GSSoundLock(sound); + sound->paused = 0; + GSSoundUnlock(sound); +} + +void GSSoundPause(GSSound sound) { + GSSoundLock(sound); + sound->paused = 1; + GSSoundUnlock(sound); +} + +void GSSoundLock(GSSound sound) { + ma_mutex_lock(&sound->mutex); +} + +void GSSoundUnlock(GSSound sound) { + ma_mutex_unlock(&sound->mutex); +} diff --git a/engine/src/sounddriver.c b/engine/src/sounddriver.c new file mode 100644 index 0000000..93cdb3c --- /dev/null +++ b/engine/src/sounddriver.c @@ -0,0 +1,61 @@ +#include + +#include + +#include + +static void data_callback(ma_device* device, void* output, const void* input, ma_uint32 frame) { + GSSoundDriver driver = device->pUserData; + + driver->callback(driver->opaque, output, frame); +} + +GSSoundDriver GSSoundDriverOpen(GSClient client, GSSoundDriverReadCallback callback, void* opaque) { + GSSoundDriver driver = malloc(sizeof(*driver)); + + memset(driver, 0, sizeof(*driver)); + + driver->engine = client->engine; + + driver->callback = callback; + driver->opaque = opaque; + + driver->config = ma_device_config_init(ma_device_type_playback); + driver->config.playback.format = ma_format_s16; + driver->config.playback.channels = 2; + driver->config.sampleRate = GSSoundDriverRate; + driver->config.dataCallback = data_callback; + driver->config.pUserData = driver; + + driver->ready = 0; + + if(ma_device_init(NULL, &driver->config, &driver->device) != MA_SUCCESS) { + GSLog(client->engine, GSLogInfo, "Failed to initialize sound driver"); + + GSSoundDriverClose(driver); + + return NULL; + } + + driver->ready = 1; + + if(ma_device_start(&driver->device) != MA_SUCCESS) { + GSLog(client->engine, GSLogInfo, "Failed to start sound driver"); + + GSSoundDriverClose(driver); + + return NULL; + } + + GSLog(client->engine, GSLogInfo, "Opened sound driver"); + + return driver; +} + +void GSSoundDriverClose(GSSoundDriver driver) { + if(driver->ready) ma_device_uninit(&driver->device); + + GSLog(driver->engine, GSLogInfo, "Closed sound driver"); + + free(driver); +} diff --git a/engine/src/soundengine.c b/engine/src/soundengine.c new file mode 100644 index 0000000..cecd045 --- /dev/null +++ b/engine/src/soundengine.c @@ -0,0 +1,94 @@ +#include + +#include +#include +#include + +#include +#include + +static void read_callback(void* opaque, GSI16* out, int frame) { + GSSoundEngine sengine = opaque; + int i; + GSNumber* buffer = malloc(sizeof(*buffer) * frame * 2); + GSI16* result = malloc(sizeof(*result) * frame * 2); + + for(i = 0; i < frame * 2; i++) buffer[i] = 0; + + GSSoundEngineLock(sengine); + for(i = 0; i < arrlen(sengine->sound); i++) { + int s; + + GSSoundLock(sengine->sound[i]); + if(sengine->sound[i]->paused || sengine->sound[i]->read == NULL) { + GSSoundUnlock(sengine->sound[i]); + continue; + } + + memset(result, 0, sizeof(*result) * frame * 2); + s = sengine->sound[i]->read(sengine->sound[i], result, frame); + if(s > 0) { + int j; + for(j = 0; j < s * 2; j++) buffer[j] += (GSNumber)result[j] / 32767; + } else if(sengine->sound[i]->loop && sengine->sound[i]->reset != NULL) { + sengine->sound[i]->reset(sengine->sound[i]); + } else { + sengine->sound[i]->paused = GSTrue; + } + GSSoundUnlock(sengine->sound[i]); + } + GSSoundEngineUnlock(sengine); + + for(i = 0; i < frame * 2; i++) out[i] = buffer[i] * 32767; + + free(result); + free(buffer); +} + +GSSoundEngine GSSoundEngineCreate(GSClient client) { + GSSoundEngine sengine = malloc(sizeof(*sengine)); + + memset(sengine, 0, sizeof(*sengine)); + + sengine->engine = client->engine; + + ma_mutex_init(&sengine->mutex); + + sengine->sound = NULL; + + if((sengine->driver = GSSoundDriverOpen(client, read_callback, sengine)) == NULL) { + GSLog(client->engine, GSLogError, "Failed to open sound driver"); + GSSoundEngineDestroy(sengine); + + return NULL; + } + + GSLog(client->engine, GSLogInfo, "Created sound engine"); + + return sengine; +} + +void GSSoundEngineDestroy(GSSoundEngine sengine) { + int i; + + if(sengine->driver != NULL) GSSoundDriverClose(sengine->driver); + + ma_mutex_uninit(&sengine->mutex); + + for(i = 0; i < arrlen(sengine->sound); i++) { + GSSoundClose(sengine->sound[i]); + } + arrfree(sengine->sound); + + GSLog(sengine->engine, GSLogInfo, "Destroyed sound engine"); + + free(sengine); +} + +void GSSoundEngineLock(GSSoundEngine sengine) { + ma_mutex_lock(&sengine->mutex); +} + +void GSSoundEngineUnlock(GSSoundEngine sengine) { + ma_mutex_unlock(&sengine->mutex); +}