diff --git a/CMakeLists.txt b/CMakeLists.txt index c7c6352..bf0c288 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ endmacro() add_subdirectory(core) add_subdirectory(audio) +add_subdirectory(sound) install( DIRECTORY include/ diff --git a/audio/pthread/body.c b/audio/pthread/body.c index 9976e15..d579f3d 100644 --- a/audio/pthread/body.c +++ b/audio/pthread/body.c @@ -22,7 +22,7 @@ static void* thread_routine(void* arg) { return NULL; } -void* MDEAudioOpen(MDEAudioHandler handler, void* user) { +MDEAudio MDEAudioOpen(MDEAudioHandler handler, void* user) { int rate = MDEAudioRate; driver_t* drv = malloc(sizeof(*drv)); memset(drv, 0, sizeof(*drv)); @@ -42,7 +42,7 @@ void* MDEAudioOpen(MDEAudioHandler handler, void* user) { return drv; } -void MDEAudioClose(void* handle) { +void MDEAudioClose(MDEAudio handle) { driver_t* drv = handle; void* ret; @@ -60,7 +60,7 @@ void MDEAudioClose(void* handle) { free(drv); } -void MDEAudioStart(void* handle) { +void MDEAudioStart(MDEAudio handle) { driver_t* drv = handle; pthread_create(&drv->thread, NULL, thread_routine, handle); diff --git a/include/MDE/Audio/Audio.h b/include/MDE/Audio/Audio.h index 3b7cfb9..0d271d8 100644 --- a/include/MDE/Audio/Audio.h +++ b/include/MDE/Audio/Audio.h @@ -1,5 +1,5 @@ -#ifndef __MDE_AUDIO_H__ -#define __MDE_AUDIO_H__ +#ifndef __MDE_AUDIO_AUDIO_H__ +#define __MDE_AUDIO_AUDIO_H__ #include @@ -12,15 +12,17 @@ extern "C" { /* always stereo, btw */ #define MDEAudioRate 48000 +typedef void* MDEAudio; + /* S16, stereo. frames*2*2 == bytes */ -typedef void (*MDEAudioHandler)(void* handle, void* user, void* data, int frames); +typedef void (*MDEAudioHandler)(MDEAudio handle, void* user, void* data, int frames); /* driver functions */ -void* MDEAudioOpen(MDEAudioHandler handler, void* user); +MDEAudio MDEAudioOpen(MDEAudioHandler handler, void* user); -void MDEAudioClose(void* handle); +void MDEAudioClose(MDEAudio handle); -void MDEAudioStart(void* handle); +void MDEAudioStart(MDEAudio handle); #ifdef __cplusplus } diff --git a/include/MDE/Foundation.h b/include/MDE/Foundation.h index 8029e2e..d881f6f 100644 --- a/include/MDE/Foundation.h +++ b/include/MDE/Foundation.h @@ -10,4 +10,7 @@ #include +#include +#include + #endif diff --git a/include/MDE/Sound/ID3.h b/include/MDE/Sound/ID3.h new file mode 100644 index 0000000..d1645fb --- /dev/null +++ b/include/MDE/Sound/ID3.h @@ -0,0 +1,18 @@ +#ifndef __MDE_SOUND_ID3_H__ +#define __MDE_SOUND_ID3_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +char* MDEID3GetTag(const char* path, const char* name, size_t* size); + +char* MDEID3GetString(const char* path, const char* name); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/MDE/Sound/Sound.h b/include/MDE/Sound/Sound.h new file mode 100644 index 0000000..3dc8e9d --- /dev/null +++ b/include/MDE/Sound/Sound.h @@ -0,0 +1,54 @@ +#ifndef __MDE_SOUND_SOUND_H__ +#define __MDE_SOUND_SOUND_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _MDESoundDriver MDESoundDriverRec, *MDESoundDriver; +typedef struct _MDESoundContext* MDESoundContext; +typedef struct _MDESound* MDESound; + +struct _MDESoundContext { + int sample_rate; + int channels; + int frames; + void* opaque; + + char* title; + char* album; + char* artist; + char* genre; +}; + +struct _MDESoundDriver { + MDESoundContext (*open)(const char* path); + void (*close)(MDESoundContext context); + int (*read)(MDESoundContext context, short* out, int frames); + void (*seek)(MDESoundContext context, int frames); +}; + +struct _MDESound { + MDESoundContext context; + MDESoundDriver driver; +}; + +extern MDESoundDriver MDESoundDriverMP3; + +MDESound MDESoundOpen(const char* path); + +int MDESoundRead(MDESound sound, short* out, int frames); + +int MDESoundReadFloat(MDESound sound, float* out, int frames); + +void MDESoundSeek(MDESound sound, int frames); + +void MDESoundClose(MDESound sound); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/sound/CMakeLists.txt b/sound/CMakeLists.txt new file mode 100644 index 0000000..9b512b5 --- /dev/null +++ b/sound/CMakeLists.txt @@ -0,0 +1,6 @@ +setup_lib(sound Sound) +target_link_libraries( + MDESound + PRIVATE + MDECore +) diff --git a/sound/id3.c b/sound/id3.c new file mode 100644 index 0000000..1d11093 --- /dev/null +++ b/sound/id3.c @@ -0,0 +1,102 @@ +#include +#include + +static unsigned int syncsafe(FILE* f) { + unsigned char d[4]; + unsigned int r = 0; + int i; + fread(d, 4, 1, f); + + for(i = 0; i < 4; i++) { + r = r << 7; + r = r | (d[i] & 127); + } + + return r; +} + +static unsigned int big(FILE* f) { + unsigned char d[4]; + unsigned int r = 0; + int i; + fread(d, 4, 1, f); + + for(i = 0; i < 4; i++) { + r = r << 8; + r = r | d[i]; + } + + return r; +} + +char* MDEID3GetTag(const char* path, const char* name, size_t* size){ + FILE* f = fopen(path, "rb"); + unsigned char* d = NULL; + char sig[3]; + unsigned char ver[2]; + unsigned char flag; + unsigned char frame[4]; + unsigned int dsz = 0; + if(f == NULL) return NULL; + + fread(sig, 3, 1, f); + if(memcmp(sig, "ID3", 3) != 0) return NULL; + + fread(ver, 2, 1, f); + if(memcmp(ver, "\x04\x00", 2) != 0 && memcmp(ver, "\x03\x00", 2) != 0) return NULL; + + fread(&flag, 1, 1, f); + if(flag & (1 << 6)) return NULL; /* FIXME */ + if(flag & (1 << 7)) return NULL; /* what even is this??? */ + + dsz = syncsafe(f); + + while(dsz > 0) { + unsigned int fsz; + + fread(frame, 4, 1, f); + if(frame[0] == 0) break; /* XXX: Is this ok? */ + + if(memcmp(ver, "\x04\x00", 2) == 0) { + fsz = syncsafe(f); + } else { + fsz = big(f); + } + + fseek(f, 2, SEEK_CUR); + if(memcmp(frame, name, strlen(name)) == 0) { + d = malloc(fsz); + fread(d, fsz, 1, f); + + *size = fsz; + break; + } else { + fseek(f, fsz, SEEK_CUR); + } + + dsz -= 4 + 4 + 2 + fsz; + } + + fclose(f); + + return d; +} + +char* MDEID3GetString(const char* path, const char* name){ + size_t sz; + unsigned char* d = MDEID3GetTag(path, name, &sz); + char* s; + if(d == NULL) return NULL; + if(d[0] == 1 || d[0] == 2){ + free(d); + return NULL; + } + + s = malloc(sz - 1 + 1); + memcpy(s, d + 1, sz - 1); + s[sz - 1] = 0; + + free(d); + + return s; +} diff --git a/sound/mp3.c b/sound/mp3.c new file mode 100644 index 0000000..7a32fef --- /dev/null +++ b/sound/mp3.c @@ -0,0 +1,54 @@ +#include +#include + +#define DR_MP3_IMPLEMENTATION +#include + +static MDESoundContext mp3_open(const char* path){ + MDESoundContext ctx = malloc(sizeof(*ctx)); + memset(ctx, 0, sizeof(*ctx)); + + ctx->title = MDEID3GetString(path, "TIT2"); + ctx->artist = MDEID3GetString(path, "TPE1"); + ctx->album = MDEID3GetString(path, "TALB"); + ctx->genre = MDEID3GetString(path, "TCON"); + + ctx->opaque = malloc(sizeof(drmp3)); + if(!drmp3_init_file(ctx->opaque, path, NULL)){ + free(ctx); + return NULL; + } + + ctx->channels = ((drmp3*)ctx->opaque)->channels; + ctx->sample_rate = ((drmp3*)ctx->opaque)->sampleRate; + ctx->frames = drmp3_get_pcm_frame_count(ctx->opaque); + + return ctx; +} + +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); +} + +static int mp3_read(MDESoundContext ctx, short* out, int frames){ + return drmp3_read_pcm_frames_s16(ctx->opaque, frames, out); +} + +static void mp3_seek(MDESoundContext ctx, int frames){ + drmp3_seek_to_pcm_frame(ctx->opaque, frames); +} + +static MDESoundDriverRec rec = { + mp3_open, + mp3_close, + mp3_read, + mp3_seek +}; +MDESoundDriver MDESoundDriverMP3 = &rec; diff --git a/sound/sound.c b/sound/sound.c new file mode 100644 index 0000000..8a191ec --- /dev/null +++ b/sound/sound.c @@ -0,0 +1,69 @@ +#include + +#define TRY_DRIVER(name) \ + if(sound->context == NULL){ \ + sound->driver = MDESoundDriver ## name; \ + sound->context = MDESoundDriver ## name->open(path); \ + } + +MDESound MDESoundOpen(const char* path){ + MDESound sound = malloc(sizeof(*sound)); + sound->context = NULL; + + TRY_DRIVER(MP3); + + if(sound->context == NULL){ + free(sound); + return NULL; + } + + if(sound->context->channels != 2 && sound->context->channels != 1){ + MDESoundClose(sound); + return NULL; + } + + return sound; +} + +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); + + return r; +} + +int MDESoundReadFloat(MDESound sound, float* out, int frames){ + short* f = malloc(sizeof(*f) * 2 * frames); + int i; + int r = MDESoundRead(sound, f, frames); + + for(i = 0; i < r * 2; i++){ + out[i] = f[i] / 32767.0; + } + + free(f); + + return r; +} + +void MDESoundSeek(MDESound sound, int frames){ + sound->driver->seek(sound->context, frames); +} + +void MDESoundClose(MDESound sound){ + sound->driver->close(sound->context); + free(sound); +}