106 lines
3.0 KiB
C
106 lines
3.0 KiB
C
#include "cell.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
#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)
|
|
|
|
|
|
|
|
|