vorbis and buffering

This commit is contained in:
Nishi 2025-11-24 22:36:29 +09:00
commit c6960ffb9f
Signed by: nishi
GPG key ID: 27EF69B208EB9343
7 changed files with 158 additions and 18 deletions

View file

@ -5,6 +5,7 @@
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#endif

View file

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

View file

@ -1,6 +1,10 @@
include(MDEPkgConfig)
setup_lib(sound Sound)
target_link_libraries(
MDESound
PRIVATE
MDECore
)
pkg_config_add(MDESound vorbisfile)

View file

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

74
sound/drv_vorbis.c Normal file
View file

@ -0,0 +1,74 @@
#include <MDE/Sound/Sound.h>
#include <MDE/Core/String.h>
#include <vorbis/vorbisfile.h>
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;

View file

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

2
sound/stb_ds.c Normal file
View file

@ -0,0 +1,2 @@
#define STB_DS_IMPLEMENTATION
#include <stb_ds.h>