introduce MDESound

This commit is contained in:
Nishi 2025-11-24 19:49:08 +09:00
commit b2e83e695d
Signed by: nishi
GPG key ID: 27EF69B208EB9343
10 changed files with 318 additions and 9 deletions

6
sound/CMakeLists.txt Normal file
View file

@ -0,0 +1,6 @@
setup_lib(sound Sound)
target_link_libraries(
MDESound
PRIVATE
MDECore
)

102
sound/id3.c Normal file
View file

@ -0,0 +1,102 @@
#include <MDE/Sound/ID3.h>
#include <MDE/Core/String.h>
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;
}

54
sound/mp3.c Normal file
View file

@ -0,0 +1,54 @@
#include <MDE/Sound/Sound.h>
#include <MDE/Sound/ID3.h>
#define DR_MP3_IMPLEMENTATION
#include <dr_mp3.h>
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;

69
sound/sound.c Normal file
View file

@ -0,0 +1,69 @@
#include <MDE/Sound/Sound.h>
#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);
}