add mod/xm

This commit is contained in:
Nishi 2026-01-27 14:36:27 +09:00
commit 1af31757fd
Signed by: nishi
GPG key ID: 27EF69B208EB9343
15 changed files with 499 additions and 47 deletions

View file

@ -69,14 +69,23 @@ int main(int argc, char** argv){
GSInit();
if((engine = GSEngineCreate(&param)) != 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);
}
}

View file

@ -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
}

View file

@ -17,6 +17,8 @@
#include <GearSrc/SkyBox.h>
#include <GearSrc/CRC.h>
#include <GearSrc/Model.h>
#include <GearSrc/SoundDriver.h>
#include <GearSrc/SoundEngine.h>
#include <GearSrc/Sound.h>
#endif

View file

@ -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
}

View file

@ -0,0 +1,20 @@
#ifndef __GEARSRC_SOUNDDRIVER_H__
#define __GEARSRC_SOUNDDRIVER_H__
#include <GearSrc/MachDep.h>
#include <GearSrc/TypeDefs.h>
#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

View file

@ -0,0 +1,20 @@
#ifndef __GEARSRC_SOUNDENGINE_H__
#define __GEARSRC_SOUNDENGINE_H__
#include <GearSrc/MachDep.h>
#include <GearSrc/TypeDefs.h>
#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

View file

@ -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 {

View file

View file

@ -6,7 +6,7 @@
#include <GearSrc/Math.h>
#include <GearSrc/Model.h>
#include <GearSrc/Version.h>
#include <GearSrc/Sound.h>
#include <GearSrc/SoundEngine.h>
#include <stb_ds.h>
@ -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;
}

View file

@ -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) {

74
engine/src/snd_mod.c Normal file
View file

@ -0,0 +1,74 @@
#include <GearSrc/Sound.h>
#include <GearSrc/SoundDriver.h>
#include <GearSrc/File.h>
#include <jar_mod.h>
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;
}

52
engine/src/snd_xm.c Normal file
View file

@ -0,0 +1,52 @@
#include <GearSrc/Sound.h>
#include <GearSrc/SoundDriver.h>
#include <GearSrc/File.h>
#include <jar_xm.h>
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;
}

View file

@ -1,48 +1,108 @@
#include <GearSrc/Sound.h>
#include <GearSrc/File.h>
#include <GearSrc/SoundEngine.h>
#include <GearSrc/Sound.h>
#include <GearSrc/Log.h>
#include <stb_ds.h>
#include <miniaudio.h>
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);
}

61
engine/src/sounddriver.c Normal file
View file

@ -0,0 +1,61 @@
#include <GearSrc/SoundDriver.h>
#include <GearSrc/Log.h>
#include <miniaudio.h>
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);
}

94
engine/src/soundengine.c Normal file
View file

@ -0,0 +1,94 @@
#include <GearSrc/SoundEngine.h>
#include <GearSrc/SoundDriver.h>
#include <GearSrc/Log.h>
#include <GearSrc/Sound.h>
#include <miniaudio.h>
#include <stb_ds.h>
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);
}