mod/xm works

This commit is contained in:
NishiOwO 2025-04-21 00:19:47 +09:00
parent ca17a29d57
commit f5036b8a0b
No known key found for this signature in database
GPG Key ID: 27EF69B208EB9343
4 changed files with 123 additions and 19 deletions

View File

@ -254,6 +254,7 @@ gf_bool_t jar_mod_setcfg(jar_mod_context_t* modctx, int samplerate, int bits, in
void jar_mod_fillbuffer(jar_mod_context_t* modctx, gf_int16_t* outbuffer, unsigned long nbsample, jar_mod_tracker_buffer_state* trkbuf);
void jar_mod_unload(jar_mod_context_t* modctx);
mulong jar_mod_load_file(jar_mod_context_t* modctx, const char* filename);
gf_bool_t jar_mod_load(jar_mod_context_t* modctx, void* mod_data, int mod_data_size);
mulong jar_mod_current_samples(jar_mod_context_t* modctx);
mulong jar_mod_max_samples(jar_mod_context_t* modctx);
void jar_mod_seek_start(jar_mod_context_t* ctx);
@ -969,7 +970,7 @@ gf_bool_t jar_mod_setcfg(jar_mod_context_t* modctx, int samplerate, int bits, in
}
/* make certain that mod_data stays in memory while playing */
static gf_bool_t jar_mod_load(jar_mod_context_t* modctx, void* mod_data, int mod_data_size) {
gf_bool_t jar_mod_load(jar_mod_context_t* modctx, void* mod_data, int mod_data_size) {
muint i, max;
unsigned short t;
sample* sptr;
@ -1090,7 +1091,7 @@ void jar_mod_fillbuffer(jar_mod_context_t* modctx, gf_int16_t* outbuffer, unsign
note* nptr;
channel* cptr;
if(modctx && outbuffer) {
if(modctx) {
if(modctx->mod_loaded) {
state_remaining_steps = 0;
@ -1247,8 +1248,10 @@ void jar_mod_fillbuffer(jar_mod_context_t* modctx, gf_int16_t* outbuffer, unsign
if(r < -32768) r = -32768;
/* Store the final sample. */
outbuffer[(i * 2)] = l;
outbuffer[(i * 2) + 1] = r;
if(outbuffer != NULL) {
outbuffer[(i * 2)] = l;
outbuffer[(i * 2) + 1] = r;
}
ll = tl;
lr = tr;
@ -1257,6 +1260,7 @@ void jar_mod_fillbuffer(jar_mod_context_t* modctx, gf_int16_t* outbuffer, unsign
modctx->last_l_sample = ll;
modctx->last_r_sample = lr;
skip:;
modctx->samplenb = modctx->samplenb + nbsample;
} else {
for(i = 0; i < nbsample; i++) {
@ -1351,14 +1355,16 @@ mulong jar_mod_current_samples(jar_mod_context_t* modctx) {
/* Works, however it is very slow, this data should be cached to ensure it is run only once per file */
mulong jar_mod_max_samples(jar_mod_context_t* ctx) {
mint buff[2];
mulong len;
mulong lastcount = ctx->loopcount;
while(ctx->loopcount <= lastcount) jar_mod_fillbuffer(ctx, buff, 1, 0);
while(ctx->loopcount <= lastcount) jar_mod_fillbuffer(ctx, NULL, 1, 0);
len = ctx->samplenb;
jar_mod_seek_start(ctx);
ctx->samplenb = 0;
ctx->tablepos = 0;
ctx->patternpos = 0;
return len;
}

View File

@ -18,6 +18,8 @@
#include <string.h>
#include <math.h>
const char* gf_audio_mod_sig[] = {"M!K!", "M.K.", "FLT4", "FLT8", "4CHN", "6CHN", "8CHN", "10CH", "12CH", "14CH", "16CH", "18CH", "20CH", "22CH", "24CH", "26CH", "28CH", "30CH", "32CH"};
void gf_audio_callback(ma_device* dev, void* output, const void* input, ma_uint32 frame) {
int i;
gf_audio_t* audio = dev->pUserData;
@ -30,7 +32,7 @@ void gf_audio_callback(ma_device* dev, void* output, const void* input, ma_uint3
}
for(i = 0; i < GF_AUDIO_MAX_DECODERS; i++) {
if(audio->decoder[i].used == 1) {
if(audio->decoder[i].used && audio->decoder[i].decoder != NULL) {
ma_uint64 readframe;
int j;
ma_int16* r = malloc(sizeof(*r) * frame * 2);
@ -41,10 +43,37 @@ void gf_audio_callback(ma_device* dev, void* output, const void* input, ma_uint3
}
free(r);
if(frame > readframe) {
ma_decoder_uninit(audio->decoder[i].decoder);
free(audio->decoder[i].decoder);
audio->decoder[i].decoder = NULL;
audio->decoder[i].used = 0;
gf_audio_decoder_destroy(&audio->decoder[i]);
}
} else if(audio->decoder[i].used && audio->decoder[i].xm != NULL) {
int j;
int gotframe;
float* r = malloc(sizeof(*r) * frame * 2);
jar_xm_generate_samples(audio->decoder[i].xm, r, frame);
gotframe = audio->decoder[i].samples > frame ? frame : audio->decoder[i].samples;
for(j = 0; j < gotframe; j++) {
tmp[2 * j + 0] += (double)r[2 * j + 0];
tmp[2 * j + 1] += (double)r[2 * j + 1];
}
free(r);
audio->decoder[i].samples -= frame;
if(audio->decoder[i].samples <= 0) {
gf_audio_decoder_destroy(&audio->decoder[i]);
}
} else if(audio->decoder[i].used && audio->decoder[i].mod != NULL) {
int j;
int gotframe;
ma_int16* r = malloc(sizeof(*r) * frame * 2);
jar_mod_fillbuffer(audio->decoder[i].mod, r, frame, NULL);
gotframe = audio->decoder[i].samples > frame ? frame : audio->decoder[i].samples;
for(j = 0; j < gotframe; j++) {
tmp[2 * j + 0] += (double)r[2 * j + 0] / 32768.0;
tmp[2 * j + 1] += (double)r[2 * j + 1] / 32768.0;
}
free(r);
audio->decoder[i].samples -= frame;
if(audio->decoder[i].samples <= 0) {
gf_audio_decoder_destroy(&audio->decoder[i]);
}
}
}
@ -60,6 +89,35 @@ int gf_audio_load(gf_audio_t* audio, const void* data, size_t size) {
int i;
for(i = 0; i < GF_AUDIO_MAX_DECODERS; i++) {
if(audio->decoder[i].used == 0) {
int xm_cond = size > 37 && memcmp(data, "Extended Module: ", 17) == 0 && ((char*)data)[37] == 0x1a;
int mod_cond = size > 1080;
if(mod_cond) {
int j;
for(j = 0; j < sizeof(gf_audio_mod_sig) / sizeof(gf_audio_mod_sig[0]); j++) {
mod_cond = mod_cond || (memcmp(data + 1080, gf_audio_mod_sig[j], 4) == 0);
}
}
if(xm_cond) {
if(jar_xm_create_context_safe(&audio->decoder[i].xm, data, size, audio->device_config.sampleRate) == 0) {
audio->decoder[i].samples = jar_xm_get_remaining_samples(audio->decoder[i].xm);
audio->decoder[i].used = 1;
return 0;
}
audio->decoder[i].xm = NULL;
} else if(mod_cond) {
audio->decoder[i].mod = malloc(sizeof(*audio->decoder[i].mod));
jar_mod_init(audio->decoder[i].mod);
jar_mod_setcfg(audio->decoder[i].mod, audio->device_config.sampleRate, 16, 1, 0, 0);
if(jar_mod_load(audio->decoder[i].mod, (void*)data, size)) {
audio->decoder[i].samples = jar_mod_max_samples(audio->decoder[i].mod);
audio->decoder[i].used = 1;
return 0;
}
free(audio->decoder[i].mod);
audio->decoder[i].mod = NULL;
}
audio->decoder[i].decoder = malloc(sizeof(*audio->decoder[i].decoder));
if(ma_decoder_init_memory(data, size, &audio->decoder[i].decoder_config, audio->decoder[i].decoder) == MA_SUCCESS) {
audio->decoder[i].used = 1;
@ -102,16 +160,20 @@ gf_audio_t* gf_audio_create(gf_engine_t* engine) {
audio->device_config = ma_device_config_init(ma_device_type_playback);
audio->device_config.playback.format = ma_format_s16;
audio->device_config.playback.channels = 2;
audio->device_config.sampleRate = 44100;
audio->device_config.sampleRate = 48000;
audio->device_config.dataCallback = gf_audio_callback;
audio->device_config.pUserData = audio;
for(i = 0; i < GF_AUDIO_MAX_DECODERS; i++) {
audio->decoder[i].used = 0;
audio->decoder[i].decoder = NULL;
audio->decoder[i].xm = NULL;
audio->decoder[i].mod = NULL;
audio->decoder[i].decoder_config = ma_decoder_config_init(audio->device_config.playback.format, audio->device_config.playback.channels, audio->device_config.sampleRate);
}
gf_audio_load_file(audio, "test.xm");
audio->device = malloc(sizeof(*audio->device));
if(ma_device_init(NULL, &audio->device_config, audio->device) != MA_SUCCESS) {
gf_log_function(engine, "Failed to open playback device", "");
@ -132,6 +194,24 @@ gf_audio_t* gf_audio_create(gf_engine_t* engine) {
return audio;
}
void gf_audio_decoder_destroy(gf_audio_decoder_t* decoder) {
if(decoder->decoder != NULL) {
ma_decoder_uninit(decoder->decoder);
free(decoder->decoder);
decoder->decoder = NULL;
}
if(decoder->xm != NULL) {
jar_xm_free_context(decoder->xm);
decoder->xm = NULL;
}
if(decoder->mod != NULL) {
jar_mod_unload(decoder->mod);
free(decoder->mod);
decoder->mod = NULL;
}
decoder->used = 0;
}
void gf_audio_destroy(gf_audio_t* audio) {
int i;
if(audio->device != NULL) {
@ -139,10 +219,7 @@ void gf_audio_destroy(gf_audio_t* audio) {
free(audio->device);
}
for(i = 0; i < GF_AUDIO_MAX_DECODERS; i++) {
if(audio->decoder[i].decoder != NULL) {
ma_decoder_uninit(audio->decoder[i].decoder);
free(audio->decoder[i].decoder);
}
gf_audio_decoder_destroy(&audio->decoder[i]);
}
gf_log_function(audio->engine, "Destroyed audio interface", "");
free(audio);

View File

@ -34,6 +34,13 @@ GF_EXPORT gf_audio_t* gf_audio_create(gf_engine_t* engine);
*/
GF_EXPORT void gf_audio_destroy(gf_audio_t* audio);
/**
* @~english
* @brief Destroy audio decoder
* @param decoder Audio decoder
*/
GF_EXPORT void gf_audio_decoder_destroy(gf_audio_decoder_t* decoder);
/**
* @~english
* @brief Load and play file

View File

@ -16,6 +16,8 @@ typedef struct gf_audio_decoder_t gf_audio_decoder_t;
/* External library */
#include <miniaudio.h>
#include <jar_xm.h>
#include <jar_mod.h>
/* Engine */
#include <gf_type/core.h>
@ -39,13 +41,25 @@ typedef struct gf_audio_decoder_t gf_audio_decoder_t;
* @var gf_audio_decoder_t::decoder
* @brief miniaudio decoder
*
* @var gf_audio_decoder_t::xm
* @brief XM context
*
* @var gf_audio_decoder_t::mod
* @brief MOD context
*
* @var gf_audio_decoder_t::samples
* @brief Remaining samples
*
* @var gf_audio_decoder_t::used
* @brief `1` if used, otherwise `0`
*/
GF_DECLARE_TYPE(audio_decoder, {
ma_decoder_config decoder_config;
ma_decoder* decoder;
int used;
ma_decoder_config decoder_config;
ma_decoder* decoder;
jar_xm_context_t* xm;
jar_mod_context_t* mod;
int samples;
int used;
});
/**