proper gc rooting

This commit is contained in:
2026-02-21 13:12:34 -06:00
parent a1fa8020c5
commit ad7ee40fe0
5 changed files with 40 additions and 25 deletions

3
dsp.c
View File

@@ -17,9 +17,8 @@ JSC_CCALL(dsp_mix_blobs,
if (!JS_IsArray(vols_arr)) return JS_ThrowTypeError(js, "volumes must be an array");
int len = 0;
JSValue len_val = JS_GetPropertyStr(js, blobs_arr, "length");
JSValue len_val = JS_CellLength(js, blobs_arr);
JS_ToInt32(js, &len, len_val);
JS_FreeValue(js, len_val);
if (len == 0) {
// Return empty stoned blob

15
flac.c
View File

@@ -28,10 +28,12 @@ static JSValue flac_make_info(JSContext *js, drflac *flac)
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));
JSValue pcm_count = JS_NewFloat64(js, (double)flac->totalPCMFrameCount);
JS_SetPropertyStr(js, obj.val, "total_pcm_frames", pcm_count);
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"));
JSValue fmt = JS_NewString(js, "s32");
JS_SetPropertyStr(js, obj.val, "format", fmt);
JS_RETURN(obj.val);
}
@@ -94,11 +96,14 @@ JSC_CCALL(flac_decode,
JS_FRAME(js);
JS_ROOT(result, flac_make_info(js, flac));
// Update format info
JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, "f32"));
// Update format info — evaluate allocating calls in separate statements
// (C argument evaluation order is unspecified; GC can invalidate .val)
JSValue fmt = JS_NewString(js, "f32");
JS_SetPropertyStr(js, result.val, "format", fmt);
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));
JSValue pcm_blob = js_new_blob_stoned_copy(js, pcm, bytes_read);
JS_SetPropertyStr(js, result.val, "pcm", pcm_blob);
free(pcm);
drflac_close(flac);
JS_RETURN(result.val);

16
mp3.c
View File

@@ -14,10 +14,12 @@ static JSValue mp3_make_info(JSContext *js, drmp3_uint32 channels, drmp3_uint32
JS_SetPropertyStr(js, obj.val, "bits_per_sample", JS_NewInt32(js, 16));
double total_frames = frames == DRMP3_UINT64_MAX ? -1.0 : (double)frames;
JS_SetPropertyStr(js, obj.val, "total_pcm_frames", JS_NewFloat64(js, total_frames));
JSValue pcm_count = JS_NewFloat64(js, total_frames);
JS_SetPropertyStr(js, obj.val, "total_pcm_frames", pcm_count);
JS_SetPropertyStr(js, obj.val, "decoded_bytes_per_frame",
JS_NewInt32(js, (int)((size_t)channels * sizeof(drmp3_int16))));
JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "s16"));
JSValue fmt = JS_NewString(js, "s16");
JS_SetPropertyStr(js, obj.val, "format", fmt);
JS_RETURN(obj.val);
}
@@ -85,11 +87,15 @@ JSC_CCALL(mp3_decode,
JS_FRAME(js);
JS_ROOT(result, mp3_make_info(js, config.channels, config.sampleRate, frames));
// Update format info
JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, "f32"));
// Update format info — evaluate allocating calls before reading .val
// (C argument evaluation order is unspecified; GC during allocation
// can move the rooted object, invalidating an already-read .val)
JSValue fmt = JS_NewString(js, "f32");
JS_SetPropertyStr(js, result.val, "format", fmt);
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, total_bytes));
JSValue pcm_blob = js_new_blob_stoned_copy(js, pcm, total_bytes);
JS_SetPropertyStr(js, result.val, "pcm", pcm_blob);
drmp3_free(pcm, NULL);
JS_RETURN(result.val);
)

View File

@@ -139,16 +139,16 @@ soundwave.create = function(opts) {
// Pull a chunk of audio from a voice, handling looping
function pull_voice_chunk(voice, frames) {
if (voice.stopped) return null
var source = voice.source
var total_frames = source.frames
var pos = voice.pos
var bytes_per_frame = player.channels * BYTES_PER_SAMPLE
var bits_per_frame = bytes_per_frame * 8
var out = Blob()
var frames_written = 0
var frames_available = null
var frames_needed = null
var frames_to_read = null
@@ -172,13 +172,13 @@ soundwave.create = function(opts) {
end_bit = (pos + frames_to_read) * bits_per_frame
chunk = source.pcm.read_blob(start_bit, end_bit)
out.write_blob(chunk)
pos += frames_to_read
frames_written += frames_to_read
}
voice.pos = pos
// Pad with silence if needed
var silence_frames = null
var silence = null
@@ -187,7 +187,7 @@ soundwave.create = function(opts) {
silence = dsp.silence(silence_frames, player.channels)
out.write_blob(silence)
}
stone(out)
return out
}
@@ -259,7 +259,7 @@ soundwave.create = function(opts) {
} else {
mixed = dsp.mix_blobs(blobs, vols)
}
player.cleanup()
return mixed
}

15
wav.c
View File

@@ -29,7 +29,8 @@ static JSValue wav_make_info(JSContext *js, drwav *wav)
JS_SetPropertyStr(js, obj.val, "sample_rate", JS_NewInt32(js, wav->sampleRate));
JS_SetPropertyStr(js, obj.val, "bits_per_sample", JS_NewInt32(js, wav->bitsPerSample));
JS_SetPropertyStr(js, obj.val, "format_tag", JS_NewInt32(js, wav->translatedFormatTag));
JS_SetPropertyStr(js, obj.val, "total_pcm_frames", JS_NewFloat64(js, (double)wav->totalPCMFrameCount));
JSValue pcm_count = JS_NewFloat64(js, (double)wav->totalPCMFrameCount);
JS_SetPropertyStr(js, obj.val, "total_pcm_frames", pcm_count);
JS_SetPropertyStr(js, obj.val, "bytes_per_frame", JS_NewInt32(js, (int)drwav_get_bytes_per_pcm_frame(wav)));
JS_RETURN(obj.val);
}
@@ -94,15 +95,19 @@ JSC_CCALL(wav_decode,
JS_FRAME(js);
JS_ROOT(result, wav_make_info(js, &wav));
// Update format info to reflect f32
JS_SetPropertyStr(js, result.val, "format", JS_NewString(js, "f32"));
// Update format info — evaluate allocating calls in separate statements
// (C argument evaluation order is unspecified; GC can invalidate .val)
JSValue fmt = JS_NewString(js, "f32");
JS_SetPropertyStr(js, result.val, "format", fmt);
JS_SetPropertyStr(js, result.val, "bytes_per_frame", JS_NewInt32(js, (int)bytes_per_frame));
if (pcm_bytes > 0) {
JS_SetPropertyStr(js, result.val, "pcm", js_new_blob_stoned_copy(js, pcm, bytes_read));
JSValue pcm_blob = js_new_blob_stoned_copy(js, pcm, bytes_read);
JS_SetPropertyStr(js, result.val, "pcm", pcm_blob);
free(pcm);
} else {
JS_SetPropertyStr(js, result.val, "pcm", js_new_blob_stoned_copy(js, NULL, 0));
JSValue pcm_blob = js_new_blob_stoned_copy(js, NULL, 0);
JS_SetPropertyStr(js, result.val, "pcm", pcm_blob);
}
drwav_uninit(&wav);