sdl audio

This commit is contained in:
2025-11-26 02:07:44 -06:00
parent 155d0bf4b5
commit fd19ecb41e
3 changed files with 375 additions and 270 deletions

View File

@@ -273,6 +273,7 @@ src += [ # engine
'qjs_sdl_video.c', 'qjs_sdl_video.c',
'qjs_sdl_surface.c', 'qjs_sdl_surface.c',
'qjs_sdl_gpu.c', 'qjs_sdl_gpu.c',
'qjs_sdl_audio.c',
'qjs_math.c', 'qjs_math.c',
'qjs_geometry.c', 'qjs_geometry.c',
'qjs_transform.c', 'qjs_transform.c',

View File

@@ -14,13 +14,8 @@ void SDL_Camera_free(JSRuntime *rt, SDL_Camera *cam)
SDL_CloseCamera(cam); SDL_CloseCamera(cam);
} }
void SDL_AudioStream_free(JSRuntime *rt, SDL_AudioStream *st) {
SDL_DestroyAudioStream(st);
}
// Class definitions for SDL types // Class definitions for SDL types
QJSCLASS(SDL_Camera,) QJSCLASS(SDL_Camera,)
QJSCLASS(SDL_AudioStream,)
// CAMERA FUNCTIONS // CAMERA FUNCTIONS
@@ -289,271 +284,8 @@ JSValue js_camera_use(JSContext *js) {
return mod; return mod;
} }
// SDL AUDIO FUNCTIONS JSValue js_sdl_use(JSContext *js) {
// Audio format lookup table and conversion functions
static const struct { const char *s; SDL_AudioFormat f; } fmt_lut[] = {
{ "u8", SDL_AUDIO_U8 }, /* Unsigned 8-bit */
{ "s8", SDL_AUDIO_S8 }, /* Signed 8-bit */
{ "s16", SDL_AUDIO_S16 }, /* Signed 16-bit, host endian */
{ "s32", SDL_AUDIO_S32 }, /* Signed 32-bit, host endian */
{ "f32", SDL_AUDIO_F32 } /* Float 32-bit, host endian */
};
static int format_str_to_enum(const char *f, SDL_AudioFormat *out)
{
struct { const char *s; SDL_AudioFormat f; } map[] = {
{"u8", SDL_AUDIO_U8 }, {"s16", SDL_AUDIO_S16},
{"s32", SDL_AUDIO_S32}, {"f32", SDL_AUDIO_F32}
};
for (size_t i=0;i<countof(map);++i)
if (!strcmp(f,map[i].s)) { *out = map[i].f; return 1; }
return 0;
}
static const char *fmt2str(SDL_AudioFormat f)
{
for (size_t i = 0; i < countof(fmt_lut); ++i)
if (fmt_lut[i].f == f) return fmt_lut[i].s;
return "unknown";
}
static JSValue audiospec2js(JSContext *js, const SDL_AudioSpec *spec)
{
JSValue o = JS_NewObject(js);
/* stringify format (u8/s16/s32/f32) */
JS_SetPropertyStr(js, o, "format",
JS_NewString(js, fmt2str(spec->format)));
JS_SetPropertyStr(js, o, "channels",
JS_NewInt32(js, spec->channels));
JS_SetPropertyStr(js, o, "samplerate",
JS_NewInt32(js, spec->freq));
return o;
}
static SDL_AudioSpec js2audiospec(JSContext *js, JSValue obj)
{
SDL_AudioSpec spec;
JSValue v;
v = JS_GetPropertyStr(js, obj, "format");
if (!JS_IsNull(v)) {
const char *s = JS_ToCString(js, v);
format_str_to_enum(s, &spec.format);
JS_FreeCString(js, s);
}
JS_FreeValue(js, v);
v = JS_GetPropertyStr(js, obj, "channels");
if (!JS_IsNull(v)) JS_ToInt32(js, &spec.channels, v);
JS_FreeValue(js, v);
v = JS_GetPropertyStr(js, obj, "samplerate");
if (!JS_IsNull(v)) JS_ToInt32(js, &spec.freq, v);
JS_FreeValue(js, v);
return spec;
}
JSC_CCALL(sdl_audio_drivers,
int num = SDL_GetNumAudioDrivers();
JSValue arr = JS_NewArray(js);
for (int i = 0; i < num; i++)
JS_SetPropertyUint32(js, arr, i, JS_NewString(js, SDL_GetAudioDriver(i)));
return arr;
)
JSC_CCALL(sdl_audio_devices,
int n;
SDL_AudioDeviceID *ids = SDL_GetAudioPlaybackDevices(&n);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < n; i++)
JS_SetPropertyUint32(js,arr,i,JS_NewString(js, SDL_GetAudioDeviceName(ids[i])));
return arr;
)
JSC_CCALL(sdl_audio_open_stream,
const char *type = JS_IsString(argv[0]) ? JS_ToCString(js, argv[0]) : NULL;
SDL_AudioDeviceID devid = !strcmp(type, "capture") ? SDL_AUDIO_DEVICE_DEFAULT_RECORDING : SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
if (type)
JS_FreeCString(js, type);
SDL_AudioStream *st;
if (JS_IsNull(argv[1]))
st = SDL_OpenAudioDeviceStream(devid, NULL, NULL, NULL);
else {
SDL_AudioSpec want = js2audiospec(js, argv[1]);
st = SDL_OpenAudioDeviceStream(devid, &want, NULL, NULL);
}
if (!st)
return JS_ThrowInternalError(js, "open failed: %s", SDL_GetError());
return SDL_AudioStream2js(js, st);
)
static const JSCFunctionListEntry js_sdl_audio_funcs[] = {
MIST_FUNC_DEF(sdl_audio, drivers, 0),
MIST_FUNC_DEF(sdl_audio, devices, 0),
MIST_FUNC_DEF(sdl_audio, open_stream, 2),
};
JSC_CCALL(sdl_audiostream_get_format,
SDL_AudioStream *as = js2SDL_AudioStream(js, self);
SDL_AudioSpec src;
SDL_AudioSpec dst;
SDL_GetAudioStreamFormat(as, &src, &dst);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "src", audiospec2js(js, &src));
JS_SetPropertyStr(js, obj, "dst", audiospec2js(js, &dst));
return obj;
)
JSC_CCALL(sdl_audiostream_set_format,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
const SDL_AudioSpec *src_ptr=NULL,*dst_ptr=NULL;
SDL_AudioSpec src={0},dst={0};
if(argc>0&&!JS_IsNull(argv[0])){
src=js2audiospec(js,argv[0]);
src_ptr=&src;
}
if(argc>1&&!JS_IsNull(argv[1])){
dst=js2audiospec(js,argv[1]);
dst_ptr=&dst;
}
if(!SDL_SetAudioStreamFormat(as,src_ptr,dst_ptr))
return JS_ThrowInternalError(js,"%s",SDL_GetError());
return JS_NULL;
)
JSC_CCALL(sdl_audiostream_resume,
SDL_AudioStream *as = js2SDL_AudioStream(js,self);
if (!SDL_ResumeAudioStreamDevice(as))
return JS_ThrowInternalError(js,"%s",SDL_GetError());
return JS_NULL;
)
JSC_CCALL(sdl_audiostream_clear,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
if (!SDL_ClearAudioStream(as))
return JS_ThrowInternalError(js,"%s",SDL_GetError());
return JS_NULL;
)
JSC_CCALL(sdl_audiostream_flush,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
if(!SDL_FlushAudioStream(as))
return JS_ThrowInternalError(js,"%s",SDL_GetError());
return JS_NULL;
)
JSC_CCALL(sdl_audiostream_available,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
Sint64 n = SDL_GetAudioStreamAvailable(as);
if(n<0) return JS_ThrowInternalError(js,"%s",SDL_GetError());
return JS_NewInt64(js,n);
)
JSC_CCALL(sdl_audiostream_queued,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
Sint64 n = SDL_GetAudioStreamQueued(as);
if(n<0) return JS_ThrowInternalError(js,"%s",SDL_GetError());
return JS_NewInt64(js,n);
)
/* ---------- data IO ---------------------------------------------------- */
JSC_CCALL(sdl_audiostream_put,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
size_t len;
void *buf = js_get_blob_data(js, &len, argv[0]);
if (!buf)
return JS_ThrowInternalError(js, "Requires array buffer.");
if (!SDL_PutAudioStreamData(as,buf,len))
return JS_ThrowInternalError(js, "%s", SDL_GetError());
return JS_NULL;
)
JSC_CCALL(sdl_audiostream_get,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
int want;
JS_ToInt32(js,&want,argv[0]);
void *data = malloc(want);
int got = SDL_GetAudioStreamData(as, data, want);
if (got<0) {
free(data);
return JS_ThrowInternalError(js,"%s",SDL_GetError());
}
JSValue ab = js_new_blob_stoned_copy(js, data, got);
free(data);
return ab;
)
JSC_CCALL(sdl_audiostream_get_gain,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
return JS_NewFloat64(js,SDL_GetAudioStreamGain(as));
)
JSC_CCALL(sdl_audiostream_set_gain,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
double g; JS_ToFloat64(js,&g,argv[0]);
SDL_SetAudioStreamGain(as,(float)g);
return JS_NULL;
)
JSC_CCALL(sdl_audiostream_get_freq_ratio,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
return JS_NewFloat64(js,SDL_GetAudioStreamFrequencyRatio(as));
)
JSC_CCALL(sdl_audiostream_set_freq_ratio,
SDL_AudioStream *as=js2SDL_AudioStream(js,self);
double r; JS_ToFloat64(js,&r,argv[0]);
SDL_SetAudioStreamFrequencyRatio(as,(float)r);
return JS_NULL;
)
/* ---------- JS export list -------------------------------------------- */
static const JSCFunctionListEntry js_SDL_AudioStream_funcs[] = {
MIST_FUNC_DEF(sdl_audiostream, get_format, 0),
MIST_FUNC_DEF(sdl_audiostream, set_format, 2),
MIST_FUNC_DEF(sdl_audiostream, resume, 0),
MIST_FUNC_DEF(sdl_audiostream, clear, 0),
MIST_FUNC_DEF(sdl_audiostream, flush, 0),
MIST_FUNC_DEF(sdl_audiostream, available, 0),
MIST_FUNC_DEF(sdl_audiostream, queued, 0),
MIST_FUNC_DEF(sdl_audiostream, put, 1),
MIST_FUNC_DEF(sdl_audiostream, get, 1),
MIST_FUNC_DEF(sdl_audiostream, set_gain, 1),
MIST_FUNC_DEF(sdl_audiostream, get_gain, 0),
MIST_FUNC_DEF(sdl_audiostream, set_freq_ratio, 1),
MIST_FUNC_DEF(sdl_audiostream, get_freq_ratio, 0),
};
JSValue js_sdl_audio_use(JSContext *js) {
if (!SDL_Init(SDL_INIT_AUDIO))
return JS_ThrowInternalError(js, "Unable to initialize audio subsystem: %s", SDL_GetError());
QJSCLASSPREP_FUNCS(SDL_AudioStream)
JSValue mod = JS_NewObject(js); JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_sdl_audio_funcs,countof(js_sdl_audio_funcs)); JS_SetPropertyStr(js, mod, "camera", js_camera_use(js));
return mod; return mod;
} }

372
source/qjs_sdl_audio.c Normal file
View File

@@ -0,0 +1,372 @@
#include "qjs_macros.h"
#include "jsffi.h"
#include "quickjs.h"
#include <SDL3/SDL.h>
#include <SDL3/SDL_audio.h>
#include "cell.h"
#define countof(x) (sizeof(x)/sizeof((x)[0]))
// Helper functions
double js2number(JSContext *js, JSValue v);
int js2bool(JSContext *js, JSValue v);
// Free functions for finalizers
void SDL_AudioStream_free(JSRuntime *rt, SDL_AudioStream *stream) {
SDL_DestroyAudioStream(stream);
}
// Class definitions
QJSCLASS(SDL_AudioStream,)
// Conversion functions
SDL_AudioFormat js2SDL_AudioFormat(JSContext *js, JSValue v) {
int fmt = js2number(js, v);
return (SDL_AudioFormat)fmt;
}
JSValue SDL_AudioFormat2js(JSContext *js, SDL_AudioFormat fmt) {
return JS_NewInt32(js, (int)fmt);
}
SDL_AudioDeviceID js2SDL_AudioDeviceID(JSContext *js, JSValue v) {
return (SDL_AudioDeviceID)js2number(js, v);
}
JSValue SDL_AudioDeviceID2js(JSContext *js, SDL_AudioDeviceID id) {
return JS_NewInt32(js, (int)id);
}
SDL_AudioSpec js2SDL_AudioSpec(JSContext *js, JSValue v) {
SDL_AudioSpec spec = {0};
JS_GETPROP(js, spec.format, v, format, SDL_AudioFormat)
JS_GETPROP(js, spec.channels, v, channels, number)
JS_GETPROP(js, spec.freq, v, freq, number)
return spec;
}
JSValue SDL_AudioSpec2js(JSContext *js, SDL_AudioSpec spec) {
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "format", SDL_AudioFormat2js(js, spec.format));
JS_SetPropertyStr(js, obj, "channels", JS_NewInt32(js, spec.channels));
JS_SetPropertyStr(js, obj, "freq", JS_NewInt32(js, spec.freq));
return obj;
}
// Enum mappings for audio formats (simplified)
JSValue js_get_audio_drivers(JSContext *js, JSValue self, int argc, JSValue *argv) {
int count = SDL_GetNumAudioDrivers();
JSValue arr = JS_NewArray(js);
for (int i = 0; i < count; i++) {
const char *driver = SDL_GetAudioDriver(i);
JS_SetPropertyUint32(js, arr, i, JS_NewString(js, driver));
}
return arr;
}
JSValue js_get_current_audio_driver(JSContext *js, JSValue self, int argc, JSValue *argv) {
const char *driver = SDL_GetCurrentAudioDriver();
return driver ? JS_NewString(js, driver) : JS_NULL;
}
JSValue js_get_audio_playback_devices(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID *devices = SDL_GetAudioPlaybackDevices(NULL);
if (!devices) return JS_NULL;
JSValue arr = JS_NewArray(js);
for (int i = 0; devices[i]; i++) {
JS_SetPropertyUint32(js, arr, i, SDL_AudioDeviceID2js(js, devices[i]));
}
SDL_free(devices);
return arr;
}
JSValue js_get_audio_recording_devices(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID *devices = SDL_GetAudioRecordingDevices(NULL);
if (!devices) return JS_NULL;
JSValue arr = JS_NewArray(js);
for (int i = 0; devices[i]; i++) {
JS_SetPropertyUint32(js, arr, i, SDL_AudioDeviceID2js(js, devices[i]));
}
SDL_free(devices);
return arr;
}
JSValue js_get_audio_device_name(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
const char *name = SDL_GetAudioDeviceName(devid);
return name ? JS_NewString(js, name) : JS_NULL;
}
JSValue js_is_audio_device_playback(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
return JS_NewBool(js, SDL_IsAudioDevicePlayback(devid));
}
JSValue js_is_audio_device_physical(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
return JS_NewBool(js, SDL_IsAudioDevicePhysical(devid));
}
JSValue js_get_audio_device_format(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
SDL_AudioSpec spec;
if (!SDL_GetAudioDeviceFormat(devid, &spec, NULL)) {
return JS_NULL;
}
return SDL_AudioSpec2js(js, spec);
}
JSValue js_open_audio_device_stream(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
SDL_AudioSpec spec = {0};
if (argc > 1) {
spec = js2SDL_AudioSpec(js, argv[1]);
}
SDL_AudioStream *stream = SDL_OpenAudioDeviceStream(devid, &spec, NULL, NULL);
if (!stream) {
return JS_ThrowInternalError(js, "Failed to open audio device stream: %s", SDL_GetError());
}
return SDL_AudioStream2js(js, stream);
}
JSValue js_create_audio_stream(JSContext *js, JSValue self, int argc, JSValue *argv) {
SDL_AudioSpec src_spec = js2SDL_AudioSpec(js, argv[0]);
SDL_AudioSpec dst_spec = js2SDL_AudioSpec(js, argv[1]);
SDL_AudioStream *stream = SDL_CreateAudioStream(&src_spec, &dst_spec);
if (!stream) {
return JS_ThrowInternalError(js, "Failed to create audio stream: %s", SDL_GetError());
}
return SDL_AudioStream2js(js, stream);
}
JSC_CCALL(audio_stream_put_data,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
size_t len;
void *data = js_get_blob_data(js, &len, argv[0]);
if (!SDL_PutAudioStreamData(stream, data, len)) {
ret = JS_ThrowInternalError(js, "Failed to put audio stream data: %s", SDL_GetError());
}
)
JSC_CCALL(audio_stream_get_data,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
int len = js2number(js, argv[0]);
void *data = malloc(len);
int got = SDL_GetAudioStreamData(stream, data, len);
if (got < 0) {
free(data);
ret = JS_ThrowInternalError(js, "Failed to get audio stream data: %s", SDL_GetError());
} else {
ret = js_new_blob_stoned_copy(js, data, got);
free(data);
}
)
JSC_CCALL(audio_stream_available,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
ret = JS_NewInt32(js, SDL_GetAudioStreamAvailable(stream));
)
JSC_CCALL(audio_stream_queued,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
ret = JS_NewInt32(js, SDL_GetAudioStreamQueued(stream));
)
JSC_CCALL(audio_stream_flush,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_FlushAudioStream(stream);
)
JSC_CCALL(audio_stream_clear,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_ClearAudioStream(stream);
)
JSC_CCALL(audio_stream_bind,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
if (!SDL_BindAudioStream(devid, stream)) {
ret = JS_ThrowInternalError(js, "Failed to bind audio stream: %s", SDL_GetError());
}
)
JSC_CCALL(audio_stream_unbind,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_UnbindAudioStream(stream);
)
JSC_CCALL(audio_stream_get_format,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_AudioSpec src, dst;
if (!SDL_GetAudioStreamFormat(stream, &src, &dst)) {
ret = JS_NULL;
} else {
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "src", SDL_AudioSpec2js(js, src));
JS_SetPropertyStr(js, obj, "dst", SDL_AudioSpec2js(js, dst));
ret = obj;
}
)
JSC_CCALL(audio_stream_get_device,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_AudioDeviceID devid = SDL_GetAudioStreamDevice(stream);
ret = SDL_AudioDeviceID2js(js, devid);
)
JSC_CCALL(audio_stream_get_gain,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
ret = JS_NewFloat64(js, SDL_GetAudioStreamGain(stream));
)
JSC_CCALL(audio_stream_set_gain,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
float gain = js2number(js, argv[0]);
if (!SDL_SetAudioStreamGain(stream, gain)) {
ret = JS_ThrowInternalError(js, "Failed to set audio stream gain: %s", SDL_GetError());
}
)
JSC_CCALL(audio_stream_set_frequency_ratio,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
float ratio = js2number(js, argv[0]);
if (!SDL_SetAudioStreamFrequencyRatio(stream, ratio)) {
ret = JS_ThrowInternalError(js, "Failed to set audio stream frequency ratio: %s", SDL_GetError());
}
)
JSC_CCALL(audio_stream_get_frequency_ratio,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
ret = JS_NewFloat64(js, SDL_GetAudioStreamFrequencyRatio(stream));
)
JSC_CCALL(audio_device_pause,
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
SDL_PauseAudioDevice(devid);
)
JSC_CCALL(audio_device_resume,
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
SDL_ResumeAudioDevice(devid);
)
JSC_CCALL(audio_device_paused,
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
ret = JS_NewBool(js, SDL_AudioDevicePaused(devid));
)
JSC_CCALL(audio_stream_device_paused,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
ret = JS_NewBool(js, SDL_AudioStreamDevicePaused(stream));
)
JSC_CCALL(audio_stream_pause_device,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_PauseAudioStreamDevice(stream);
)
JSC_CCALL(audio_stream_resume_device,
SDL_AudioStream *stream = js2SDL_AudioStream(js, self);
SDL_ResumeAudioStreamDevice(stream);
)
JSC_CCALL(audio_device_close,
SDL_AudioDeviceID devid = js2SDL_AudioDeviceID(js, argv[0]);
SDL_CloseAudioDevice(devid);
)
JSValue js_load_wav(JSContext *js, JSValue self, int argc, JSValue *argv) {
const char *path = JS_ToCString(js, argv[0]);
SDL_AudioSpec spec;
Uint8 *data;
Uint32 len;
if (!SDL_LoadWAV(path, &spec, &data, &len)) {
JS_FreeCString(js, path);
return JS_ThrowInternalError(js, "Failed to load WAV: %s", SDL_GetError());
}
JS_FreeCString(js, path);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "spec", SDL_AudioSpec2js(js, spec));
JS_SetPropertyStr(js, obj, "data", js_new_blob_stoned_copy(js, data, len));
SDL_free(data);
return obj;
}
JSC_CCALL(convert_audio_samples,
SDL_AudioSpec src_spec = js2SDL_AudioSpec(js, argv[0]);
SDL_AudioSpec dst_spec = js2SDL_AudioSpec(js, argv[1]);
size_t src_len;
void *src_data = js_get_blob_data(js, &src_len, argv[2]);
Uint8 *dst_data = NULL;
int dst_len = 0;
if (!SDL_ConvertAudioSamples(&src_spec, src_data, (int)src_len, &dst_spec, &dst_data, &dst_len)) {
ret = JS_ThrowInternalError(js, "Failed to convert audio samples: %s", SDL_GetError());
} else {
ret = js_new_blob_stoned_copy(js, dst_data, dst_len);
SDL_free(dst_data);
}
)
JSC_CCALL(mix_audio,
SDL_AudioFormat format = js2SDL_AudioFormat(js, argv[0]);
size_t len;
void *dst = js_get_blob_data(js, &len, argv[1]);
void *src = js_get_blob_data(js, &len, argv[2]);
float volume = js2number(js, argv[3]);
SDL_MixAudio(dst, src, format, len, volume);
)
// Function list for SDL_AudioStream
static const JSCFunctionListEntry js_SDL_AudioStream_funcs[] = {
JS_CFUNC_DEF("put", 1, js_audio_stream_put_data),
JS_CFUNC_DEF("get", 1, js_audio_stream_get_data),
JS_CFUNC_DEF("available", 0, js_audio_stream_available),
JS_CFUNC_DEF("queued", 0, js_audio_stream_queued),
JS_CFUNC_DEF("flush", 0, js_audio_stream_flush),
JS_CFUNC_DEF("clear", 0, js_audio_stream_clear),
JS_CFUNC_DEF("bind", 1, js_audio_stream_bind),
JS_CFUNC_DEF("unbind", 0, js_audio_stream_unbind),
JS_CFUNC_DEF("get_format", 0, js_audio_stream_get_format),
JS_CFUNC_DEF("get_device", 0, js_audio_stream_get_device),
JS_CGETSET_DEF("gain", js_audio_stream_get_gain, js_audio_stream_set_gain),
JS_CGETSET_DEF("frequency_ratio", js_audio_stream_get_frequency_ratio, js_audio_stream_set_frequency_ratio),
JS_CFUNC_DEF("pause_device", 0, js_audio_stream_pause_device),
JS_CFUNC_DEF("resume_device", 0, js_audio_stream_resume_device),
JS_CFUNC_DEF("device_paused", 0, js_audio_stream_device_paused),
};
// Main function list
static const JSCFunctionListEntry js_sdl_audio_funcs[] = {
JS_CFUNC_DEF("get_drivers", 0, js_get_audio_drivers),
JS_CFUNC_DEF("get_current_driver", 0, js_get_current_audio_driver),
JS_CFUNC_DEF("get_playback_devices", 0, js_get_audio_playback_devices),
JS_CFUNC_DEF("get_recording_devices", 0, js_get_audio_recording_devices),
JS_CFUNC_DEF("get_device_name", 1, js_get_audio_device_name),
JS_CFUNC_DEF("is_playback_device", 1, js_is_audio_device_playback),
JS_CFUNC_DEF("is_physical_device", 1, js_is_audio_device_physical),
JS_CFUNC_DEF("get_device_format", 1, js_get_audio_device_format),
JS_CFUNC_DEF("open_device_stream", 1, js_open_audio_device_stream),
JS_CFUNC_DEF("create_stream", 2, js_create_audio_stream),
JS_CFUNC_DEF("pause_device", 1, js_audio_device_pause),
JS_CFUNC_DEF("resume_device", 1, js_audio_device_resume),
JS_CFUNC_DEF("device_paused", 1, js_audio_device_paused),
JS_CFUNC_DEF("close_device", 1, js_audio_device_close),
JS_CFUNC_DEF("load_wav", 1, js_load_wav),
JS_CFUNC_DEF("convert_samples", 3, js_convert_audio_samples),
JS_CFUNC_DEF("mix_audio", 4, js_mix_audio),
};
// Use function
JSValue js_sdl_audio_use(JSContext *js) {
JS_NewClassID(&js_SDL_AudioStream_id);
JS_NewClass(JS_GetRuntime(js), js_SDL_AudioStream_id, &js_SDL_AudioStream_class);
JSValue proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, proto, js_SDL_AudioStream_funcs, countof(js_SDL_AudioStream_funcs));
JS_SetClassProto(js, js_SDL_AudioStream_id, proto);
JSValue export = JS_NewObject(js);
JS_SetPropertyFunctionList(js, export, js_sdl_audio_funcs, countof(js_sdl_audio_funcs));
return export;
}