#include "cell.h" #include #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) { JS_FRAME(js); JS_ROOT(obj, JS_NewObject(js)); JS_SetPropertyStr(js, obj.val, "channels", JS_NewInt32(js, flac->channels)); JS_SetPropertyStr(js, obj.val, "sample_rate", JS_NewInt32(js, flac->sampleRate)); JS_SetPropertyStr(js, obj.val, "bits_per_sample", JS_NewInt32(js, flac->bitsPerSample)); JS_SetPropertyStr(js, obj.val, "total_pcm_frames", JS_NewFloat64(js, (double)flac->totalPCMFrameCount)); JS_SetPropertyStr(js, obj.val, "decoded_bytes_per_frame", JS_NewInt32(js, (int)((size_t)flac->channels * sizeof(drflac_int32)))); JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "s32")); JS_RETURN(obj.val); } 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); JS_FRAME(js); JS_ROOT(result, flac_make_info(js, flac)); // Update format info JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, "f32")); JS_SetPropertyStr(js, result.val, "decoded_bytes_per_frame", JS_NewInt32(js, (int)bytes_per_frame)); JS_SetPropertyStr(js, result.val, "pcm", js_new_blob_stoned_copy(js, pcm, bytes_read)); free(pcm); drflac_close(flac); JS_RETURN(result.val); ) static const JSCFunctionListEntry js_flac_funcs[] = { MIST_FUNC_DEF(flac, info, 1), MIST_FUNC_DEF(flac, decode, 1) }; CELL_USE_FUNCS(js_flac_funcs)