Files
soundwave/flac.c
2025-12-19 00:18:53 -06:00

116 lines
3.1 KiB
C

#include "cell.h"
#include <stdlib.h>
#define DR_FLAC_IMPLEMENTATION
#include "dr_flac.h"
static int flac_calc_size(drflac *flac, drflac_uint64 frames, size_t *out_bytes)
{
if (!flac || !out_bytes)
return -1;
if (flac->channels == 0)
return -1;
size_t bytes_per_frame = (size_t)flac->channels * sizeof(drflac_int32);
if (frames > SIZE_MAX / bytes_per_frame)
return -1;
*out_bytes = (size_t)(frames * bytes_per_frame);
return 0;
}
static JSValue flac_make_info(JSContext *js, drflac *flac)
{
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "channels", JS_NewInt32(js, flac->channels));
JS_SetPropertyStr(js, obj, "sample_rate", JS_NewInt32(js, flac->sampleRate));
JS_SetPropertyStr(js, obj, "bits_per_sample", JS_NewInt32(js, flac->bitsPerSample));
JS_SetPropertyStr(js, obj, "total_pcm_frames", JS_NewFloat64(js, (double)flac->totalPCMFrameCount));
JS_SetPropertyStr(js, obj, "decoded_bytes_per_frame",
JS_NewInt32(js, (int)((size_t)flac->channels * sizeof(drflac_int32))));
JS_SetPropertyStr(js, obj, "format", JS_NewString(js, "s32"));
return obj;
}
JSC_CCALL(flac_info,
size_t len;
void *data = js_get_blob_data(js, &len, argv[0]);
if (data == -1)
return JS_EXCEPTION;
if (!data)
return JS_ThrowReferenceError(js, "invalid FLAC data");
drflac *flac = drflac_open_memory(data, len, NULL);
if (!flac)
return JS_ThrowReferenceError(js, "invalid FLAC data");
JSValue info = flac_make_info(js, flac);
drflac_close(flac);
return info;
)
JSC_CCALL(flac_decode,
size_t len;
void *data = js_get_blob_data(js, &len, argv[0]);
if (data == -1)
return JS_EXCEPTION;
if (!data)
return JS_ThrowTypeError(js, "flac.decode expects a blob with data");
drflac *flac = drflac_open_memory(data, len, NULL);
if (!flac)
return JS_ThrowReferenceError(js, "invalid FLAC data");
size_t pcm_bytes;
size_t bytes_per_frame = (size_t)flac->channels * sizeof(float);
if (flac->totalPCMFrameCount > SIZE_MAX / bytes_per_frame) {
drflac_close(flac);
return JS_ThrowRangeError(js, "FLAC data too large to decode");
}
pcm_bytes = (size_t)(flac->totalPCMFrameCount * bytes_per_frame);
float *pcm = NULL;
if (pcm_bytes > 0) {
pcm = malloc(pcm_bytes);
if (!pcm) {
drflac_close(flac);
return JS_ThrowOutOfMemory(js);
}
}
drflac_uint64 frames_read = 0;
if (pcm_bytes > 0)
frames_read = drflac_read_pcm_frames_f32(flac, flac->totalPCMFrameCount, pcm);
size_t bytes_read = 0;
if (pcm_bytes > 0)
bytes_read = (size_t)(frames_read * bytes_per_frame);
JSValue result = flac_make_info(js, flac);
// Update format info
JS_SetPropertyStr(js, result, "format", JS_NewString(js, "f32"));
JS_SetPropertyStr(js, result, "decoded_bytes_per_frame", JS_NewInt32(js, (int)bytes_per_frame));
JSValue blob = js_new_blob_stoned_copy(js, pcm, bytes_read);
JS_SetPropertyStr(js, result, "pcm", blob);
free(pcm);
drflac_close(flac);
return result;
)
static const JSCFunctionListEntry js_flac_funcs[] = {
MIST_FUNC_DEF(flac, info, 1),
MIST_FUNC_DEF(flac, decode, 1)
};
CELL_USE_FUNCS(js_flac_funcs)