sdl audio
This commit is contained in:
@@ -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',
|
||||||
|
|||||||
272
source/qjs_sdl.c
272
source/qjs_sdl.c
@@ -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
372
source/qjs_sdl_audio.c
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user