initial add
This commit is contained in:
171
audio.c
Normal file
171
audio.c
Normal file
@@ -0,0 +1,171 @@
|
||||
#define SOKOL_AUDIO_IMPL
|
||||
#include "sokol/sokol_audio.h"
|
||||
|
||||
#include "cell.h"
|
||||
|
||||
// Helper to check bool property
|
||||
static int JS_GETBOOL(JSContext *js, JSValue obj, const char *prop) {
|
||||
JSValue v = JS_GetPropertyStr(js, obj, prop);
|
||||
int ret = JS_ToBool(js, v);
|
||||
JS_FreeValue(js, v);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Global JS callback for audio streaming
|
||||
static JSContext *g_audio_js = NULL;
|
||||
static JSValue g_audio_callback = JS_NULL;
|
||||
|
||||
// Audio stream callback that calls into JS
|
||||
static void audio_stream_callback(float *buffer, int num_frames, int num_channels) {
|
||||
if (!g_audio_js || JS_IsNull(g_audio_callback)) {
|
||||
// Fill with silence
|
||||
for (int i = 0; i < num_frames * num_channels; i++) {
|
||||
buffer[i] = 0.0f;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a blob from the buffer data
|
||||
int num_samples = num_frames * num_channels;
|
||||
size_t buffer_size = num_samples * sizeof(float);
|
||||
JSValue blob = js_new_blob_stoned_copy(g_audio_js, buffer, buffer_size);
|
||||
|
||||
JSValue args[3] = {
|
||||
blob,
|
||||
JS_NewInt32(g_audio_js, num_frames),
|
||||
JS_NewInt32(g_audio_js, num_channels)
|
||||
};
|
||||
|
||||
JSValue result = JS_Call(g_audio_js, g_audio_callback, JS_NULL, 3, args);
|
||||
|
||||
JS_FreeValue(g_audio_js, args[0]);
|
||||
JS_FreeValue(g_audio_js, args[1]);
|
||||
JS_FreeValue(g_audio_js, args[2]);
|
||||
JS_FreeValue(g_audio_js, result);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// SETUP / SHUTDOWN
|
||||
// ============================================================================
|
||||
|
||||
// audio.setup(desc) - Initialize sokol_audio
|
||||
// desc: { sample_rate, num_channels, buffer_frames, packet_frames, num_packets, stream_cb }
|
||||
JSC_CCALL(audio_setup,
|
||||
saudio_desc desc = {0};
|
||||
|
||||
if (argc > 0 && JS_IsObject(argv[0])) {
|
||||
JSValue cfg = argv[0];
|
||||
|
||||
JS_GETPROP(js, desc.sample_rate, cfg, sample_rate, number);
|
||||
JS_GETPROP(js, desc.num_channels, cfg, num_channels, number);
|
||||
JS_GETPROP(js, desc.buffer_frames, cfg, buffer_frames, number);
|
||||
JS_GETPROP(js, desc.packet_frames, cfg, packet_frames, number);
|
||||
JS_GETPROP(js, desc.num_packets, cfg, num_packets, number);
|
||||
|
||||
// Check for stream callback
|
||||
JSValue cb_val = JS_GetPropertyStr(js, cfg, "stream_cb");
|
||||
if (JS_IsFunction(js, cb_val)) {
|
||||
// Store the callback globally
|
||||
if (!JS_IsNull(g_audio_callback)) {
|
||||
JS_FreeValue(g_audio_js, g_audio_callback);
|
||||
}
|
||||
g_audio_js = js;
|
||||
g_audio_callback = JS_DupValue(js, cb_val);
|
||||
desc.stream_cb = audio_stream_callback;
|
||||
}
|
||||
JS_FreeValue(js, cb_val);
|
||||
}
|
||||
|
||||
saudio_setup(&desc);
|
||||
)
|
||||
|
||||
// audio.shutdown() - Shutdown sokol_audio
|
||||
JSC_CCALL(audio_shutdown,
|
||||
saudio_shutdown();
|
||||
|
||||
// Clean up callback
|
||||
if (!JS_IsNull(g_audio_callback)) {
|
||||
JS_FreeValue(g_audio_js, g_audio_callback);
|
||||
g_audio_callback = JS_NULL;
|
||||
g_audio_js = NULL;
|
||||
}
|
||||
)
|
||||
|
||||
// audio.is_valid() - Check if audio is initialized
|
||||
JSC_CCALL(audio_is_valid,
|
||||
return JS_NewBool(js, saudio_isvalid());
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// QUERY
|
||||
// ============================================================================
|
||||
|
||||
// audio.sample_rate() - Get actual sample rate
|
||||
JSC_CCALL(audio_sample_rate,
|
||||
return JS_NewInt32(js, saudio_sample_rate());
|
||||
)
|
||||
|
||||
// audio.buffer_frames() - Get actual buffer size in frames
|
||||
JSC_CCALL(audio_buffer_frames,
|
||||
return JS_NewInt32(js, saudio_buffer_frames());
|
||||
)
|
||||
|
||||
// audio.channels() - Get actual number of channels
|
||||
JSC_CCALL(audio_channels,
|
||||
return JS_NewInt32(js, saudio_channels());
|
||||
)
|
||||
|
||||
// audio.suspended() - Check if audio is suspended (WebAudio only)
|
||||
JSC_CCALL(audio_suspended,
|
||||
return JS_NewBool(js, saudio_suspended());
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// PUSH MODEL
|
||||
// ============================================================================
|
||||
|
||||
// audio.expect() - Get number of frames that can be pushed
|
||||
JSC_CCALL(audio_expect,
|
||||
return JS_NewInt32(js, saudio_expect());
|
||||
)
|
||||
|
||||
// audio.push(samples) - Push audio samples (Float32Array)
|
||||
// Returns number of frames actually pushed
|
||||
JSC_CCALL(audio_push,
|
||||
size_t data_size;
|
||||
void *data_ptr = js_get_blob_data(js, &data_size, argv[0]);
|
||||
|
||||
if (data_ptr && data_ptr != (void*)-1) {
|
||||
int num_channels = saudio_channels();
|
||||
int num_frames = data_size / (sizeof(float) * num_channels);
|
||||
int pushed = saudio_push((const float*)data_ptr, num_frames);
|
||||
|
||||
return JS_NewInt32(js, pushed);
|
||||
}
|
||||
|
||||
return JS_NewInt32(js, 0);
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// MODULE INIT
|
||||
// ============================================================================
|
||||
|
||||
static const JSCFunctionListEntry js_audio_funcs[] = {
|
||||
JS_CFUNC_DEF("setup", 1, js_audio_setup),
|
||||
JS_CFUNC_DEF("shutdown", 0, js_audio_shutdown),
|
||||
JS_CFUNC_DEF("is_valid", 0, js_audio_is_valid),
|
||||
|
||||
JS_CFUNC_DEF("sample_rate", 0, js_audio_sample_rate),
|
||||
JS_CFUNC_DEF("buffer_frames", 0, js_audio_buffer_frames),
|
||||
JS_CFUNC_DEF("channels", 0, js_audio_channels),
|
||||
JS_CFUNC_DEF("suspended", 0, js_audio_suspended),
|
||||
|
||||
JS_CFUNC_DEF("expect", 0, js_audio_expect),
|
||||
JS_CFUNC_DEF("push", 1, js_audio_push),
|
||||
};
|
||||
|
||||
CELL_USE_INIT(
|
||||
JSValue audio = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, audio, js_audio_funcs, sizeof(js_audio_funcs)/sizeof(js_audio_funcs[0]));
|
||||
return audio;
|
||||
)
|
||||
Reference in New Issue
Block a user