diff --git a/include/MDE/MachDep.h b/include/MDE/MachDep.h index f3bd172..249344f 100644 --- a/include/MDE/MachDep.h +++ b/include/MDE/MachDep.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #endif diff --git a/include/MDE/Sound/Sound.h b/include/MDE/Sound/Sound.h index 3dc8e9d..9659ebe 100644 --- a/include/MDE/Sound/Sound.h +++ b/include/MDE/Sound/Sound.h @@ -10,6 +10,7 @@ extern "C" { typedef struct _MDESoundDriver MDESoundDriverRec, *MDESoundDriver; typedef struct _MDESoundContext* MDESoundContext; typedef struct _MDESound* MDESound; +typedef struct _MDESoundBuffer MDESoundBuffer; struct _MDESoundContext { int sample_rate; @@ -30,12 +31,20 @@ struct _MDESoundDriver { void (*seek)(MDESoundContext context, int frames); }; +struct _MDESoundBuffer { + short* data; + int frames; + int seek; +}; + struct _MDESound { MDESoundContext context; MDESoundDriver driver; + MDESoundBuffer* buffer; }; extern MDESoundDriver MDESoundDriverMP3; +extern MDESoundDriver MDESoundDriverVorbis; MDESound MDESoundOpen(const char* path); diff --git a/sound/CMakeLists.txt b/sound/CMakeLists.txt index 9b512b5..ea19e15 100644 --- a/sound/CMakeLists.txt +++ b/sound/CMakeLists.txt @@ -1,6 +1,10 @@ +include(MDEPkgConfig) + setup_lib(sound Sound) target_link_libraries( MDESound PRIVATE MDECore ) + +pkg_config_add(MDESound vorbisfile) diff --git a/sound/mp3.c b/sound/drv_mp3.c similarity index 87% rename from sound/mp3.c rename to sound/drv_mp3.c index 7a32fef..5b7e3f9 100644 --- a/sound/mp3.c +++ b/sound/drv_mp3.c @@ -27,11 +27,6 @@ static MDESoundContext mp3_open(const char* path){ } static void mp3_close(MDESoundContext ctx){ - if(ctx->title != NULL) free(ctx->title); - if(ctx->artist != NULL) free(ctx->artist); - if(ctx->album != NULL) free(ctx->album); - if(ctx->genre != NULL) free(ctx->genre); - drmp3_uninit(ctx->opaque); free(ctx->opaque); free(ctx); diff --git a/sound/drv_vorbis.c b/sound/drv_vorbis.c new file mode 100644 index 0000000..cb6ba8d --- /dev/null +++ b/sound/drv_vorbis.c @@ -0,0 +1,74 @@ +#include +#include + +#include + +static MDESoundContext vorbis_open(const char* path){ + MDESoundContext ctx; + FILE* f = fopen(path, "rb"); + vorbis_comment* comments; + if(f == NULL) return NULL; + + ctx = malloc(sizeof(*ctx)); + memset(ctx, 0, sizeof(*ctx)); + + ctx->opaque = malloc(sizeof(OggVorbis_File)); + if(ov_open_callbacks(f, ctx->opaque, NULL, 0, OV_CALLBACKS_DEFAULT) < 0){ + free(ctx->opaque); + free(ctx); + fclose(f); + return NULL; + } + + comments = ov_comment(ctx->opaque, -1); + if(comments != NULL){ + int i; + for(i = 0; i < comments->comments; i++){ + char* s = MDEStringDuplicate(comments->user_comments[i]); + char* p = strchr(s, '='); + if(p != NULL){ + p[0] = 0; + if(strcasecmp(s, "TITLE") == 0){ + ctx->title = MDEStringDuplicate(p + 1); + }else if(strcasecmp(s, "ALBUM") == 0){ + ctx->album = MDEStringDuplicate(p + 1); + }else if(strcasecmp(s, "ARTIST") == 0){ + ctx->artist = MDEStringDuplicate(p + 1); + }else if(strcasecmp(s, "GENRE") == 0){ + ctx->genre = MDEStringDuplicate(p + 1); + } + } + + free(s); + } + } + + ctx->channels = ov_info(ctx->opaque, -1)->channels; + ctx->sample_rate = ov_info(ctx->opaque, -1)->rate; + ctx->frames = ov_pcm_total(ctx->opaque, -1); + + return ctx; +} + +static void vorbis_close(MDESoundContext ctx){ + ov_clear(ctx->opaque); + free(ctx->opaque); + free(ctx); +} + +static int vorbis_read(MDESoundContext ctx, short* out, int frames){ + int n; + return ov_read(ctx->opaque, (char*)out, frames * 2 * ctx->channels, 0, 2, 1, &n) / 2 / ctx->channels; +} + +static void vorbis_seek(MDESoundContext ctx, int frames){ + ov_pcm_seek(ctx->opaque, frames); +} + +static MDESoundDriverRec rec = { + vorbis_open, + vorbis_close, + vorbis_read, + vorbis_seek +}; +MDESoundDriver MDESoundDriverVorbis = &rec; diff --git a/sound/sound.c b/sound/sound.c index 8a191ec..6d6bd43 100644 --- a/sound/sound.c +++ b/sound/sound.c @@ -1,5 +1,7 @@ #include +#include + #define TRY_DRIVER(name) \ if(sound->context == NULL){ \ sound->driver = MDESoundDriver ## name; \ @@ -9,8 +11,10 @@ MDESound MDESoundOpen(const char* path){ MDESound sound = malloc(sizeof(*sound)); sound->context = NULL; + sound->buffer = NULL; TRY_DRIVER(MP3); + TRY_DRIVER(Vorbis); if(sound->context == NULL){ free(sound); @@ -26,23 +30,63 @@ MDESound MDESoundOpen(const char* path){ } int MDESoundRead(MDESound sound, short* out, int frames){ - short* f = malloc(sizeof(*f) * sound->context->channels * frames); - int r = sound->driver->read(sound->context, f, frames); - int i; - if(sound->context->channels == 2){ - for(i = 0; i < r * 2; i++){ - out[i] = f[i]; - } - }else if(sound->context->channels == 1){ - for(i = 0; i < r; i++){ - out[i * 2 + 0] = f[i]; - out[i * 2 + 1] = f[i]; - } - r *= 2; - } - free(f); + int total = frames; + int seek = 0; - return r; + while(arrlen(sound->buffer) > 0 && total > 0){ + int fr = (sound->buffer[0].frames - sound->buffer[0].seek); + if(fr > total) fr = total; + + memcpy(out + seek * 2, sound->buffer[0].data + sound->buffer[0].seek * 2, 2 * 2 * fr); + + sound->buffer[0].seek += fr; + if(sound->buffer[0].seek == sound->buffer[0].frames){ + free(sound->buffer[0].data); + arrdel(sound->buffer, 0); + } + total -= fr; + seek += fr; + } + + while(total > 0){ + int i; + short* buf = malloc(sizeof(*buf) * 2 * frames); + short* f = malloc(sizeof(*f) * sound->context->channels * frames); + int r = sound->driver->read(sound->context, f, frames); + if(sound->context->channels == 2){ + for(i = 0; i < r * 2; i++){ + buf[i] = f[i]; + } + }else if(sound->context->channels == 1){ + for(i = 0; i < r; i++){ + buf[i * 2 + 0] = f[i]; + buf[i * 2 + 1] = f[i]; + } + } + free(f); + + if(r == 0){ + free(buf); + break; + }else if(total >= r){ + memcpy(out + seek * 2, buf, r * 2 * 2); + seek += r; + total -= r; + free(buf); + }else if(total < r){ + MDESoundBuffer buffer; + buffer.data = buf; + buffer.seek = total; + buffer.frames = r; + arrput(sound->buffer, buffer); + + memcpy(out + seek * 2, buf, total * 2 * 2); + seek += total; + total -= total; + } + } + + return seek; } int MDESoundReadFloat(MDESound sound, float* out, int frames){ @@ -64,6 +108,17 @@ void MDESoundSeek(MDESound sound, int frames){ } void MDESoundClose(MDESound sound){ + int i; + if(sound->context->title != NULL) free(sound->context->title); + if(sound->context->artist != NULL) free(sound->context->artist); + if(sound->context->album != NULL) free(sound->context->album); + if(sound->context->genre != NULL) free(sound->context->genre); + sound->driver->close(sound->context); + + for(i = 0; i < arrlen(sound->buffer); i++){ + free(sound->buffer[i].data); + } + arrfree(sound->buffer); free(sound); } diff --git a/sound/stb_ds.c b/sound/stb_ds.c new file mode 100644 index 0000000..689615c --- /dev/null +++ b/sound/stb_ds.c @@ -0,0 +1,2 @@ +#define STB_DS_IMPLEMENTATION +#include