audio processing

This commit is contained in:
2025-12-01 09:55:52 -06:00
parent 91626b8efd
commit 345ddebfc7
14 changed files with 27334 additions and 13 deletions

12645
audio/dr_flac.h Normal file

File diff suppressed because it is too large Load Diff

5374
audio/dr_mp3.h Normal file

File diff suppressed because it is too large Load Diff

9009
audio/dr_wav.h Normal file

File diff suppressed because it is too large Load Diff

2
audio/dsp.c Normal file
View File

@@ -0,0 +1,2 @@
// dsp.c

102
audio/flac.c Normal file
View File

@@ -0,0 +1,102 @@
#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)
return JS_ThrowTypeError(js, "flac.info expects a blob");
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)
return JS_ThrowTypeError(js, "flac.decode expects a blob");
drflac *flac = drflac_open_memory(data, len, NULL);
if (!flac)
return JS_ThrowReferenceError(js, "invalid FLAC data");
size_t pcm_bytes;
if (flac_calc_size(flac, flac->totalPCMFrameCount, &pcm_bytes) != 0) {
drflac_close(flac);
return JS_ThrowRangeError(js, "FLAC data too large to decode");
}
drflac_int32 *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_s32(flac, flac->totalPCMFrameCount, pcm);
size_t bytes_read = 0;
if (pcm_bytes > 0)
bytes_read = (size_t)(frames_read * flac->channels * sizeof(drflac_int32));
JSValue result = flac_make_info(js, flac);
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)

91
audio/mp3.c Normal file
View File

@@ -0,0 +1,91 @@
#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)
return JS_ThrowTypeError(js, "mp3.info expects a blob");
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)
return JS_ThrowTypeError(js, "mp3.decode expects a blob");
drmp3_config config;
drmp3_uint64 frames = 0;
drmp3_int16 *pcm = drmp3_open_memory_and_read_pcm_frames_s16(data, len, &config, &frames, NULL);
if (!pcm)
return JS_ThrowReferenceError(js, "failed to decode MP3 data");
size_t total_bytes;
if (mp3_calc_bytes(config.channels, frames, &total_bytes) != 0) {
drmp3_free(pcm, NULL);
return JS_ThrowRangeError(js, "MP3 output too large");
}
JSValue result = mp3_make_info(js, config.channels, config.sampleRate, frames);
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)

104
audio/wav.c Normal file
View File

@@ -0,0 +1,104 @@
#include "cell.h"
#include <stdlib.h>
#define DR_WAV_IMPLEMENTATION
#include "dr_wav.h"
static int wav_calc_size(drwav *wav, drwav_uint64 frames, size_t *out_bytes)
{
if (!wav || !out_bytes)
return -1;
size_t bytes_per_frame = drwav_get_bytes_per_pcm_frame(wav);
if (bytes_per_frame == 0)
return -1;
if (frames > SIZE_MAX / bytes_per_frame)
return -1;
*out_bytes = (size_t)(frames * bytes_per_frame);
return 0;
}
static JSValue wav_make_info(JSContext *js, drwav *wav)
{
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "channels", JS_NewInt32(js, wav->channels));
JS_SetPropertyStr(js, obj, "sample_rate", JS_NewInt32(js, wav->sampleRate));
JS_SetPropertyStr(js, obj, "bits_per_sample", JS_NewInt32(js, wav->bitsPerSample));
JS_SetPropertyStr(js, obj, "format_tag", JS_NewInt32(js, wav->translatedFormatTag));
JS_SetPropertyStr(js, obj, "total_pcm_frames", JS_NewFloat64(js, (double)wav->totalPCMFrameCount));
JS_SetPropertyStr(js, obj, "bytes_per_frame", JS_NewInt32(js, (int)drwav_get_bytes_per_pcm_frame(wav)));
return obj;
}
JSC_CCALL(wav_info,
size_t len;
void *data = js_get_blob_data(js, &len, argv[0]);
if (!data)
return JS_ThrowTypeError(js, "wav.info expects a blob");
drwav wav;
if (!drwav_init_memory(&wav, data, len, NULL))
return JS_ThrowReferenceError(js, "invalid WAV data");
JSValue info = wav_make_info(js, &wav);
drwav_uninit(&wav);
return info;
)
JSC_CCALL(wav_decode,
size_t len;
void *data = js_get_blob_data(js, &len, argv[0]);
if (!data)
return JS_ThrowTypeError(js, "wav.decode expects a blob");
drwav wav;
if (!drwav_init_memory(&wav, data, len, NULL))
return JS_ThrowReferenceError(js, "invalid WAV data");
size_t pcm_bytes;
if (wav_calc_size(&wav, wav.totalPCMFrameCount, &pcm_bytes) != 0) {
drwav_uninit(&wav);
return JS_ThrowRangeError(js, "WAV data too large");
}
void *pcm = NULL;
if (pcm_bytes > 0) {
pcm = malloc(pcm_bytes);
if (!pcm) {
drwav_uninit(&wav);
return JS_ThrowOutOfMemory(js);
}
}
drwav_uint64 frames_read = 0;
if (pcm_bytes > 0)
frames_read = drwav_read_pcm_frames(&wav, wav.totalPCMFrameCount, pcm);
size_t bytes_read = 0;
if (pcm_bytes > 0) {
size_t bytes_per_frame = drwav_get_bytes_per_pcm_frame(&wav);
bytes_read = (size_t)(frames_read * bytes_per_frame);
}
JSValue result = wav_make_info(js, &wav);
if (pcm_bytes > 0) {
JSValue blob = js_new_blob_stoned_copy(js, pcm, bytes_read);
JS_SetPropertyStr(js, result, "pcm", blob);
free(pcm);
} else {
JS_SetPropertyStr(js, result, "pcm", js_new_blob_stoned_copy(js, NULL, 0));
}
drwav_uninit(&wav);
return result;
)
static const JSCFunctionListEntry js_wav_funcs[] = {
MIST_FUNC_DEF(wav, info, 1),
MIST_FUNC_DEF(wav, decode, 1)
};
CELL_USE_FUNCS(js_wav_funcs)

View File

@@ -1,7 +1,6 @@
#include "sdl.h"
#include "cell.h"
#include "stb_ds.h"
#include "qjs_actor.h"
#include <SDL3/SDL.h>

View File

@@ -97,7 +97,7 @@ static void render_frame(plm_t *mpeg, plm_frame_t *frame, void *d) {
JSValue ret = JS_Call(ds->js, cb, JS_NULL, 1, s);
JS_FreeValue(ds->js,cb);
free(rgb);
uncaught_exception(ds->js,ret);
// TODO: raise exception
}
JSC_CCALL(os_make_video,

View File

@@ -109,7 +109,7 @@ function decode_image(bytes, ext)
function create_image(path){
try{
def bytes = io.slurpbytes(path);
def bytes = io.slurp(path);
let raw = decode_image(bytes, path.ext());
@@ -378,7 +378,7 @@ graphics.get_font = function get_font(path) {
var fontstr = `${fullpath}.${size}`
if (fontcache[fontstr]) return fontcache[fontstr]
var data = io.slurpbytes(fullpath)
var data = io.slurp(fullpath)
var font = new staef.font(data, size)
fontcache[fontstr] = font

View File

@@ -1,6 +1,5 @@
#include "cell.h"
#include "stb_ds.h"
#include "qjs_actor.h"
#include "wota.h"
#include <SDL3/SDL.h>

View File

@@ -1,6 +1,4 @@
#include "cell.h"
#include "qjs_actor.h"
#include "prosperon.h"
#include <SDL3/SDL.h>

View File

@@ -1,10 +1,10 @@
var soloud = use('soloud')
var tween = use('prosperon/tween')
var io = use('cellfs')
var res = use('resources')
var doc = use('doc')
soloud.init()
var wav = use('audio/wav')
var mp3 = use('audio/mp3')
var flac = use('audio/flac')
var audio = {}
var pcms = {}
@@ -16,7 +16,7 @@ var voices = []
audio.pcm = function pcm(file) {
file = res.find_sound(file); if (!file) return
if (pcms[file]) return pcms[file]
var buf = io.slurpbytes(file)
var buf = io.slurp(file)
return pcms[file] = soloud.load_wav_mem(buf)
}

View File

@@ -44,8 +44,6 @@ void sprite_apply(sprite *sp)
.y = -k.y * s + c };
}
// Sprite class definitions
static JSClassID js_sprite_id;
static void js_sprite_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) {