#include "cell.h" #include #define DR_MP3_IMPLEMENTATION #include "dr_mp3.h" static JSValue mp3_make_info(JSContext *js, drmp3_uint32 channels, drmp3_uint32 sample_rate, drmp3_uint64 frames) { JSValue obj = JS_NewObject(js); JS_SetPropertyStr(js, obj, "channels", JS_NewInt32(js, channels)); JS_SetPropertyStr(js, obj, "sample_rate", JS_NewInt32(js, sample_rate)); JS_SetPropertyStr(js, obj, "bits_per_sample", JS_NewInt32(js, 16)); double total_frames = frames == DRMP3_UINT64_MAX ? -1.0 : (double)frames; JS_SetPropertyStr(js, obj, "total_pcm_frames", JS_NewFloat64(js, total_frames)); JS_SetPropertyStr(js, obj, "decoded_bytes_per_frame", JS_NewInt32(js, (int)((size_t)channels * sizeof(drmp3_int16)))); JS_SetPropertyStr(js, obj, "format", JS_NewString(js, "s16")); return obj; } JSC_CCALL(mp3_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 MP3 data"); drmp3 mp3; if (!drmp3_init_memory(&mp3, data, len, NULL)) return JS_ThrowReferenceError(js, "invalid MP3 data"); drmp3_uint32 channels = mp3.channels; drmp3_uint32 sample_rate = mp3.sampleRate; drmp3_uint64 frames = mp3.totalPCMFrameCount; if (frames == DRMP3_UINT64_MAX) frames = drmp3_get_pcm_frame_count(&mp3); JSValue info = mp3_make_info(js, channels, sample_rate, frames); drmp3_uninit(&mp3); return info; ) static int mp3_calc_bytes(drmp3_uint32 channels, drmp3_uint64 frames, size_t *out_bytes) { if (!out_bytes || channels == 0) return -1; size_t bytes_per_frame = (size_t)channels * sizeof(drmp3_int16); if (frames > SIZE_MAX / bytes_per_frame) return -1; *out_bytes = (size_t)(frames * bytes_per_frame); return 0; } JSC_CCALL(mp3_decode, 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 MP3 data"); drmp3_config config; drmp3_uint64 frames = 0; float *pcm = drmp3_open_memory_and_read_pcm_frames_f32(data, len, &config, &frames, NULL); if (!pcm) return JS_ThrowReferenceError(js, "failed to decode MP3 data"); size_t bytes_per_frame = (size_t)config.channels * sizeof(float); size_t total_bytes; if (frames > SIZE_MAX / bytes_per_frame) { drmp3_free(pcm, NULL); return JS_ThrowRangeError(js, "MP3 output too large"); } total_bytes = (size_t)(frames * bytes_per_frame); JSValue result = mp3_make_info(js, config.channels, config.sampleRate, frames); // 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, total_bytes); JS_SetPropertyStr(js, result, "pcm", blob); drmp3_free(pcm, NULL); return result; ) static const JSCFunctionListEntry js_mp3_funcs[] = { MIST_FUNC_DEF(mp3, info, 1), MIST_FUNC_DEF(mp3, decode, 1) }; CELL_USE_FUNCS(js_mp3_funcs)