ref: 1e9b7af7dee79ea1611241649a7124cb2c957e9b
parent: 2b93fd2cd792cfc97b836c991d01a76f00f7ef7e
author: qwx <qwx@sciops.net>
date: Mon Mar 2 19:38:49 EST 2026
sdl2: add wip audio stream api in future, should be merged with older api, which is entirely removed from sdl3
--- a/include/npe/SDL2/SDL_audio.h
+++ b/include/npe/SDL2/SDL_audio.h
@@ -32,7 +32,15 @@
typedef struct SDL_AudioSpec SDL_AudioSpec;
typedef int SDL_AudioDeviceID;
+typedef struct SDL_AudioStream SDL_AudioStream;
+#pragma incomplete SDL_AudioStream
+
+typedef void (*SDL_AudioStreamCallback)(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount);
+
+extern SDL_AudioDeviceID SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
+extern SDL_AudioDeviceID SDL_AUDIO_DEVICE_DEFAULT_RECORDING;
+
struct SDL_AudioSpec {void (*callback)(void *, Uint8 *, int);
void *userdata;
@@ -69,5 +77,12 @@
int SDL_BuildAudioCVT(SDL_AudioCVT *cvt, SDL_AudioFormat src_format, Uint8 src_channels, int src_rate, SDL_AudioFormat dst_format, Uint8 dst_channels, int dst_rate);
int SDL_ConvertAudio(SDL_AudioCVT *cvt);
+
+int SDL_AudioStreamPut(SDL_AudioStream *stream, const void *buf, int len);
+int SDL_AudioStreamAvailable(SDL_AudioStream *stream);
+int SDL_AudioStreamGet(SDL_AudioStream *stream, void *buf, int len);
+void SDL_AudioStreamClear(SDL_AudioStream *stream);
+void SDL_FreeAudioStream(SDL_AudioStream *stream);
+SDL_AudioStream* SDL_NewAudioStream(SDL_AudioFormat src, Uint8 src_ch, int src_rate, SDL_AudioFormat dst, Uint8 dst_ch, int dst_rate);
#endif
--- a/libnpe_sdl2/_sdl.h
+++ b/libnpe_sdl2/_sdl.h
@@ -33,5 +33,7 @@
extern struct npe_sdl npe_sdl;
int npe_sdl_init_input(void);
+int npe_sdl_init_audio(void);
+void npe_sdl_kill_audio(void);
void *npe_sdl_scale(u32int *src, int iw, int ih, u32int *dst, int ow, int oh);
int npe_sdl_windowresized(int*, int*);
--- /dev/null
+++ b/libnpe_sdl2/audiostm.c
@@ -1,0 +1,358 @@
+#include "_sdl.h"
+
+/* FIXME: merge with other api */
+
+SDL_AudioDeviceID SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK = -1; /* 0 is null/invalid */
+SDL_AudioDeviceID SDL_AUDIO_DEVICE_DEFAULT_RECORDING = -1;
+
+struct SDL_AudioStream {+ QLock;
+ int fd;
+ int type;
+ char *name;
+ SDL_AudioStreamCallback fn;
+ void *aux;
+ SDL_AudioSpec;
+ int delay;
+ int tail;
+ int framesz;
+ int nframes;
+ uchar *buf;
+ int avail;
+ int paused; /* ugh */
+ Channel *c;
+ Channel *pausec;
+};
+static SDL_AudioStream *devs;
+static Channel *ach;
+static int ndevs;
+
+int
+SDL_AudioStreamAvailable(SDL_AudioStream *as)
+{+ return as->avail;
+}
+
+void
+SDL_AudioStreamClear(SDL_AudioStream *as)
+{+ qlock(as);
+ as->avail = 0;
+ qunlock(as);
+}
+
+int
+SDL_AudioStreamGet(SDL_AudioStream *as, void *buf, int n)
+{+ uchar *p;
+
+ if(n <= 0 || as->avail < as->framesz)
+ return 0;
+ if(as->avail < n)
+ n = as->avail;
+ else if(n > as->framesz)
+ n = as->framesz;
+ qlock(as);
+ p = as->buf + as->tail * as->framesz;
+ memcpy(buf, p, n);
+ as->avail -= n;
+ as->tail = (as->tail + 1) % as->nframes;
+ qunlock(as);
+ return n;
+}
+
+int
+SDL_AudioStreamPut(SDL_AudioStream *as, const void *buf, int n)
+{+ int i, m;
+ uchar *p, *t, *e;
+
+ if(as->buf == nil)
+ return -1;
+ i = as->tail; /* FIXME: actually head */
+ for(; n>0; n-=m){+ p = as->buf + i * as->framesz;
+ t = p + as->avail;
+ e = as->buf + as->nframes * as->framesz;
+ m = e - t < n ? e - t : n;
+ qlock(as);
+ memcpy(t, buf, m);
+ as->avail += m;
+ qunlock(as);
+ while(as->avail >= as->framesz){+ if(send(as->c, &i) < 0)
+ return -1; /* FIXME: recovery? */
+ i = (i + 1) % as->nframes;
+ qlock(as);
+ as->avail -= as->framesz;
+ qunlock(as);
+ }
+ }
+ return 0;
+}
+
+static void
+arproc(void *arg)
+{+ int i, n;
+ uchar *p;
+ SDL_AudioStream *as, *ad, *ae;
+
+ for(ad=nil, as=devs, ae=as+ndevs; as<ae; as++)
+ if(strcmp(as->name, "/dev/audio") == 0){+ ad = as;
+ break;
+ }
+ as = arg;
+ i = 0;
+ for(;;){+ if(as->paused)
+ recvul(as->pausec);
+ p = as->buf + i * as->framesz;
+ if((n = read(as->fd, p, as->framesz)) < 0) /* FIXME: no eof? */
+ break;
+ if(ad->fd >= 0){+ fprint(2, "wrote directly %d\n", n);
+ write(ad->fd, p, n);
+ continue;
+ }
+ if(n == 0)
+ continue;
+ qlock(as);
+ if(as->fd < 0){+ qunlock(as);
+ break;
+ }
+ as->avail += n;
+ if(as->avail > as->nframes * as->framesz)
+ as->avail = as->nframes * as->framesz;
+ nbsend(ach, &n);
+ i = (i + 1) % as->nframes;
+ if(i == as->tail)
+ as->tail = (i + 1) % as->nframes;
+ qunlock(as);
+ }
+ qlock(as);
+ chanfree(as->pausec);
+ chanfree(as->c);
+ as->pausec = as->c = nil;
+ free(as->buf);
+ as->buf = nil;
+ qunlock(as);
+}
+
+static void
+awproc(void *arg)
+{+ int i, n;
+ uchar *p;
+ SDL_AudioStream *as;
+
+ as = arg;
+ for(;;){+ if(as->paused)
+ recvul(as->pausec);
+ if(recv(as->c, &i) < 0)
+ break;
+ n = as->framesz;
+ p = as->buf + i * n;
+ fprint(2, "write %d to %s\n", n, as->name);
+ if(write(as->fd, p, n) != n){+ fprint(2, "awproc: %r\n");
+ break;
+ }
+ }
+ qlock(as);
+ chanfree(as->pausec);
+ chanfree(as->c);
+ as->pausec = as->c = nil;
+ free(as->buf);
+ as->buf = nil;
+ qunlock(as);
+}
+
+/* FIXME: bullshit */
+static void
+acbproc(void *arg)
+{+ int n;
+ SDL_AudioStream *as, *ae;
+ Channel *c;
+
+ c = arg;
+ for(;;){+ if(recv(c, &n) < 0)
+ break;
+ for(as=devs, ae=as+ndevs; as<ae; as++)
+ if(as->fd >= 0 && as->fn != nil)
+ as->fn(as->aux, as, n, as->avail);
+ }
+ chanfree(c);
+}
+
+SDL_AudioStream* SDL_NewAudioStream(SDL_AudioFormat s, Uint8 sch, int srate, SDL_AudioFormat d, Uint8 dch, int drate)
+{+ SDL_AudioDeviceID id;
+ SDL_AudioStream *as;
+
+ /* FIXME */
+ id = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
+ if(--id < 0 || id >= ndevs){+ werrstr("invalid device id %d", id);+ return nil;
+ }
+ as = devs + id;
+ /* FIXME: duplicate entries for rw devices? */
+ //if(as->fd >= 0 || !canqlock(as)){+ if(as->fd >= 0){+ werrstr("in use");+ return nil;
+ }
+ if((as->fd = open(as->name, as->type)) < 0)
+ return nil;
+ as->aux = nil;
+ as->tail = 0;
+ if((as->buf = mallocz(as->nframes * as->framesz, 1)) == nil)
+ goto Err;
+ if((as->c = chancreate(sizeof(int), 8)) == nil)
+ goto Err;
+ if((as->pausec = chancreate(sizeof(int), 0)) == nil)
+ goto Err;
+ if(proccreate(as->type != OREAD ? awproc : arproc, as, 8192) < 0)
+ goto Err;
+ USED(s, sch, srate); /* FIXME: format conversion */
+ USED(d, dch, drate);
+ as->fn = nil; /* FIXME */
+ return as;
+Err:
+ if(as->c != nil){+ chanfree(as->c);
+ as->c = nil;
+ }
+ if(as->pausec != nil){+ chanfree(as->pausec);
+ as->pausec = nil;
+ }
+ close(as->fd);
+ as->fd = -1;
+ free(as->buf);
+ return nil;
+}
+
+void
+SDL_ClearAudioStream(SDL_AudioStream *as)
+{+ if(as->fd < 0)
+ return;
+ // FIXME
+}
+
+void
+SDL_FreeAudioStream(SDL_AudioStream *as)
+{+ if(as->fd < 0)
+ return;
+ qlock(as);
+ close(as->fd);
+ as->fd = -1;
+ chanclose(as->c);
+ chanclose(as->pausec);
+ as->fn = nil;
+ as->aux = nil;
+ qunlock(as);
+}
+
+static int
+probe(void)
+{+ int i, n, dfd, type;
+ ulong len;
+ char *name, dir[] = "/dev/";
+ SDL_AudioStream as;
+ Dir *d;
+
+ if((dfd = open(dir, OREAD)) < 0)
+ return -1;
+ while((n = dirread(dfd, &d)) > 0){+ for(i = 0; i < n; i++){+ len = strlen(d[i].name);
+ if(d[i].mode & DMDIR
+ || len < 5
+ || strncmp(d[i].name, "audio", 5) != 0
+ || strncmp(d[i].name+5, "ctl", 3) == 0
+ || strncmp(d[i].name+5, "stat", 4) == 0)
+ continue;
+ if((name = smprint("%s%s", dir, d[i].name)) == nil){+ free(d);
+ return -1;
+ }
+ type = -1;
+ if(access(name, AREAD) == 0)
+ type = OREAD;
+ if(access(name, AWRITE) == 0){+ /*
+ if(type != -1)
+ type = ORDWR;
+ else
+ */
+ type = OWRITE;
+ }
+ if(type == -1){+ free(name);
+ continue;
+ }
+ if(strcmp(d[i].name, "audio") == 0){+ if(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK == -1 && type != OREAD)
+ SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK = ndevs + 1;
+ if(SDL_AUDIO_DEVICE_DEFAULT_RECORDING == -1 && type != OWRITE)
+ SDL_AUDIO_DEVICE_DEFAULT_RECORDING = ndevs + 1;
+ }
+ memset(&as, 0, sizeof as);
+ as.fd = -1;
+ as.type = type;
+ as.name = name;
+ /* FIXME: read */
+ as.freq = 44100;
+ as.channels = 2;
+ as.format = AUDIO_S16LSB;
+ as.delay = 1764;
+ as.framesz = as.delay * 2 * as.channels;
+ as.nframes = 8;
+ if((devs = realloc(devs, (ndevs+1) * sizeof *devs)) == nil){+ free(name);
+ free(d);
+ return -1; /* FIXME: devs is fucked now, we'll just segfault */
+ }
+ devs[ndevs++] = as;
+ }
+ free(d);
+ }
+ close(dfd);
+ if(ndevs == 0){+ werrstr("no devices found");+ return -1;
+ }
+ return 0;
+}
+
+void
+npe_sdl_kill_audio(void)
+{+ SDL_AudioStream *as, *ae;
+
+ chanclose(ach);
+ for(as=devs, ae=as+ndevs; as<ae; as++)
+ SDL_FreeAudioStream(as);
+}
+
+int
+npe_sdl_init_audio(void)
+{+ if(probe() < 0)
+ return -1;
+ if((ach = chancreate(sizeof(int), 16)) == nil)
+ return -1;
+ if(proccreate(acbproc, ach, 8192) < 0)
+ return -1;
+ return 0;
+}
--- a/libnpe_sdl2/mkfile
+++ b/libnpe_sdl2/mkfile
@@ -7,6 +7,7 @@
OFILES=\
audio.$O\
+ audiostm.$O\
events.$O\
image.$O\
rwops.$O\
--- a/libnpe_sdl2/sdl2.c
+++ b/libnpe_sdl2/sdl2.c
@@ -56,7 +56,8 @@
SDL_InitSubSystem(int mask)
{/* FIXME implement */
- USED(mask);
+ if(mask & SDL_INIT_AUDIO && npe_sdl_init_audio() < 0)
+ return -1;
return 0;
}
@@ -64,7 +65,8 @@
SDL_QuitSubSystem(int mask)
{/* FIXME implement */
- USED(mask);
+ if(mask & SDL_INIT_AUDIO)
+ npe_sdl_kill_audio();
return 0;
}
@@ -233,6 +235,8 @@
goto err;
if(npe_sdl_init_input() != 0)
goto err;
+ if(mask & SDL_INIT_AUDIO && npe_sdl_init_audio() < 0)
+ goto err;
npe_sdl.scale = 1;
physw = Dx(screen->r);
physh = Dy(screen->r);
@@ -483,6 +487,7 @@
SDL_Quit(void)
{/* FIXME deinitialize */
+ npe_sdl_kill_audio();
}
void
--
⑨