audio working from soloud -> sdl3
Some checks failed
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled

This commit is contained in:
2025-04-29 12:43:25 -05:00
parent 2bd93ff9e0
commit 7152ae093e
6 changed files with 247 additions and 24 deletions

View File

@@ -16,6 +16,7 @@ audio.pcm = function pcm(file)
file = res.find_sound(file);
if (!file) throw new Error(`Could not findfile ${file}`);
if (pcms[file]) return pcms[file];
var bytes = io.slurpbytes(file)
var newpcm = soloud.load_wav_mem(io.slurpbytes(file));
pcms[file] = newpcm;
return newpcm;
@@ -73,4 +74,33 @@ audio.music = function music(file, fade = 0.5) {
};
audio.music[doc.sym] = `Play the given music file, with an optional cross fade. The song will loop. When this is invoked again, the previous music is replaced.`
var ss = use('sdl_audio')
var feeder = ss.open_stream("playback")
feeder.set_format({format:"f32", channels:2,samplerate:44100})
feeder.resume()
var FRAMES = 1024
var CHANNELS = 2
var BYTES_PER_F = 4
var SAMPLES = FRAMES * CHANNELS
var CHUNK_BYTES = FRAMES * CHANNELS * BYTES_PER_F
var mixview = new Float32Array(FRAMES*CHANNELS)
var mixbuf = mixview.buffer
function pump()
{
if (feeder.queued() < CHUNK_BYTES*3) {
var mm = soloud.mix(FRAMES)
feeder.put(mm)
}
$_.delay(pump, 1/240)
}
pump()
return audio;

View File

@@ -33,6 +33,8 @@
#include "qjs_soloud.h"
#include "qjs_qr.h"
#include <signal.h>
void gui_input(SDL_Event *e);
#ifdef _WIN32
@@ -3135,15 +3137,48 @@ static const JSCFunctionListEntry js_renderer_ctx_funcs[] = {
MIST_FUNC_DEF(renderer, make_sprite_mesh, 2),
};
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;
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)
@@ -3198,9 +3233,14 @@ JSC_CCALL(sdl_audio_open_stream,
if (type)
JS_FreeCString(js, type);
// SDL_AudioSpec want = js2audiospec(js, argv[1]);
SDL_AudioStream *st;
SDL_AudioStream *st = SDL_OpenAudioDeviceStream(devid, NULL, NULL, NULL);
if (JS_IsUndefined(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());
@@ -3214,6 +3254,145 @@ static const JSCFunctionListEntry js_sdl_audio_funcs[] = {
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_IsUndefined(argv[0])){
src=js2audiospec(js,argv[0]);
src_ptr=&src;
}
if(argc>1&&!JS_IsUndefined(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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
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_UNDEFINED;
)
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_GetArrayBuffer(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_UNDEFINED;
)
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_NewArrayBufferCopy(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_UNDEFINED;
)
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_UNDEFINED;
)
/* ---------- 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),
};
// GPU API
JSC_CCALL(gpu_claim_window,
SDL_GPUDevice *gpu = js2SDL_GPUDevice(js,self);
@@ -7264,6 +7443,8 @@ void ffi_load(JSContext *js)
QJSCLASSPREP_NO_FUNCS(SDL_Cursor)
QJSCLASSPREP_FUNCS(SDL_Camera)
QJSCLASSPREP_FUNCS(SDL_AudioStream)
QJSCLASSPREP_FUNCS(renderer_ctx)
QJSCLASSPREP_FUNCS(SDL_GPUDevice)

View File

@@ -325,7 +325,9 @@ void set_actor_state(prosperon_rt *actor)
void actor_turn(prosperon_rt *actor, int greedy)
{
SDL_LockMutex(actor->turn);
#ifdef TRACY_ENABLE
TracyCFiberEnter(actor->id);
#endif
SDL_LockMutex(actor->msg_mutex);
actor->state = ACTOR_RUNNING;
@@ -412,13 +414,17 @@ void actor_turn(prosperon_rt *actor, int greedy)
goto EVENT;
END:
#ifdef TRACY_ENABLE
TracyCFiberLeave(actor->id);
#endif
SDL_UnlockMutex(actor->turn);
set_actor_state(actor);
return;
KILL:
#ifdef TRACY_ENABLE
TracyCFiberLeave(actor->id);
#endif
SDL_UnlockMutex(actor->turn);
actor_free(actor);
}

View File

@@ -1,4 +1,13 @@
#include <tracy/TracyC.h>
#ifdef TRACY_ENABLE
#include <tracy/TracyC.h>
#else
/* Provide harmless stubs when Tracy is not in use so the rest of the code
can still call the hooks unconditionally. */
#define TracyCAllocN(ptr, size, name) ((void)0)
#define TracyCFreeN(ptr, name) ((void)0)
#define TracyCFiberEnter(name) ((void)0)
#define TracyCFiberLeave(name) ((void)0)
#endif
#define MIST_CFUNC_DEF(name, length, func1, props) { name, props, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } }

View File

@@ -75,7 +75,7 @@ JSCLASS(Bus, Bus_destroy)
static JSValue js_soloud_make(JSContext *js, JSValue self, int argc, JSValue *argv)
{
soloud = Soloud_create();
Soloud_initEx(soloud, SOLOUD_CLIP_ROUNDOFF, SOLOUD_AUTO, SOLOUD_AUTO, SOLOUD_AUTO, SOLOUD_AUTO);
Soloud_initEx(soloud, SOLOUD_CLIP_ROUNDOFF, SOLOUD_NULLDRIVER, 44100, SOLOUD_AUTO, 2);
JSValue obj = JS_NewObject(js);
JS_SetPropertyStr(js, obj, "channels", JS_NewFloat64(js, Soloud_getBackendChannels(soloud)));
JS_SetPropertyStr(js, obj, "samplerate", JS_NewFloat64(js, Soloud_getBackendSamplerate(soloud)));
@@ -93,10 +93,11 @@ static JSValue js_soloud_play(JSContext *js, JSValue self, int argc, JSValue *ar
static JSValue js_soloud_mix(JSContext *js, JSValue self, int argc, JSValue *argv)
{
size_t len;
void *data = JS_GetArrayBuffer(js, &len, argv[0]);
Soloud_mix(soloud, data, js2number(js,argv[1]));
return JS_UNDEFINED;
int req;
JS_ToInt32(js, &req, argv[0]);
float *buf = malloc(2*req*sizeof(float));
Soloud_mix(soloud, buf, req);
return JS_NewArrayBufferCopy(js, buf, 2*req*sizeof(float));
}
// Create a voice from a WAV file

View File

@@ -64,12 +64,8 @@ function loop()
}
var sound = use('sound')
prosperon.myguy = sound.play("test.mp3")
var ss = use('sdl_audio')
console.log(ss.drivers())
console.log(ss.devices())
var feeder = ss.open_stream("playback", {format: "f32", channels:2, freq:48000})
//prosperon.myguy = sound.play('test.mp3')
loop()