ref: cdc06f177f6e2b9f4a4e354ac10dabfae0fd459a
parent: 26ce37c49642e2875a36650734b9619b21c49be5
author: qwx <qwx@sciops.net>
date: Fri Mar 6 06:44:33 EST 2026
sdl2: audio streaming fixes and format conversion - disable regular api while using streaming (temporary) - implement format conversion for writers via libpcm - misc fixes
--- a/libnpe_sdl2/audio.c
+++ b/libnpe_sdl2/audio.c
@@ -112,6 +112,10 @@
{Audiodev *a;
+ /* FIXME */
+ extern int audiostreaming;
+ if(audiostreaming)
+ return;
a = &au[id];
if(a->paused && !pause){if(a->pid < 0)
--- a/libnpe_sdl2/audiostm.c
+++ b/libnpe_sdl2/audiostm.c
@@ -1,12 +1,21 @@
#include "_sdl.h"
+#include <pcm.h>
/* FIXME: merge with other api */
+int audiostreaming;
SDL_AudioDeviceID SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK = -1; /* 0 is null/invalid */
SDL_AudioDeviceID SDL_AUDIO_DEVICE_DEFAULT_RECORDING = -1;
+enum{+ Delayms = 40,
+ Nframes = 16,
+};
+
struct SDL_AudioStream {QLock;
+ Rendez;
+ int closing;
int fd;
int type;
char *name;
@@ -14,130 +23,165 @@
void *aux;
SDL_AudioSpec;
int delay;
- int tail;
int framesz;
int nframes;
uchar *buf;
- int avail;
+ vlong bufsz;
+ uchar *outbuf;
+ vlong outsz;
+ Along avail;
+ int tail;
int paused; /* ugh */
- Channel *c;
- Channel *pausec;
+ Pcmconv *conv;
};
static SDL_AudioStream *devs;
static Channel *ach;
static int ndevs;
+static struct {+ char *spec;
+ int ssz; /* samples size */
+}fmts[AUDIO_NUM_FORMATS] = {+ [AUDIO_U8] = {"u8", 1},+ [AUDIO_S8] = {"s8", 1},+ [AUDIO_U16LSB] = {"u16", 2},+ [AUDIO_S16LSB] = {"s16", 2},+ [AUDIO_U16MSB] = {"U16", 2},+ [AUDIO_S16MSB] = {"S16", 2},+ [AUDIO_S32LSB] = {"s32", 4},+ [AUDIO_S32MSB] = {"S32", 4},+ [AUDIO_F32LSB] = {"f32", 4},+ [AUDIO_F32MSB] = {"F32", -1}, /* FIXME big endian f32 not supported by pcmconv */+};
+
int
SDL_AudioStreamAvailable(SDL_AudioStream *as)
{- return as->avail;
+ return agetl(&as->avail);
}
void
SDL_AudioStreamClear(SDL_AudioStream *as)
{- qlock(as);
- as->avail = 0;
- qunlock(as);
+ long n;
+
+ n = 0;
+ aswapl(&as->avail, n);
}
int
SDL_AudioStreamGet(SDL_AudioStream *as, void *buf, int n)
{- uchar *p;
+ int t;
- if(n <= 0 || as->avail < as->framesz)
+ if(as->fd < 0 || as->closing)
return 0;
- if(as->avail < n)
- n = as->avail;
- else if(n > as->framesz)
+ if(n <= 0)
+ return 0;
+ if(n > as->framesz)
n = as->framesz;
+ if(agetl(&as->avail) < n)
+ return 0;
qlock(as);
- p = as->buf + as->tail * as->framesz;
- memcpy(buf, p, n);
- as->avail -= n;
- as->tail = (as->tail + 1) % as->nframes;
+ aincl(&as->avail, -n);
+ t = as->tail;
+ if(t + n > as->bufsz)
+ n = as->bufsz - t;
+ memcpy(buf, as->buf + t, n);
+ t += n;
+ if(t >= as->bufsz)
+ t -= as->bufsz;
+ as->tail = t;
qunlock(as);
return n;
}
+/* FIXME: any reason to have .avail as along now? recheck once rest done */
+/* FIXME: make sure streams are unidirectional; dup fd's if we do rw */
int
-SDL_AudioStreamPut(SDL_AudioStream *as, const void *buf, int n)
+SDL_AudioStreamPut(SDL_AudioStream *as, void *buf, int n)
{- int i, m;
- uchar *p, *t, *e;
+ int m, k, t;
+ uchar *p, *s, *e;
- if(as->buf == nil)
+ if(as->fd < 0 || as->closing)
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;
+ e = as->buf + as->bufsz;
+ for(s=buf; n>0; n-=m, s+=m){+ m = n < as->framesz ? n : as->framesz;
+ t = as->tail;
+ p = as->buf + t;
+ if(p + m > e)
+ m = e - p;
+ while((k = as->bufsz - agetl(&as->avail)) == 0){qlock(as);
- as->avail -= as->framesz;
+ rwakeupall(as);
+ rsleep(as);
qunlock(as);
}
+ if(k < m)
+ m = k;
+ memcpy(p, s, m);
+ t += m;
+ if(t == as->bufsz)
+ t = 0;
+ as->tail = t;
+ aincl(&as->avail, m);
+ if(canqlock(as)){+ rwakeup(as);
+ qunlock(as);
+ }
}
return 0;
}
+/* FIXME: conv + rendez */
static void
arproc(void *arg)
{- int i, n;
- uchar *p;
- SDL_AudioStream *as, *ad, *ae;
+ int n, m, t;
+ uchar *p, *e;
+ SDL_AudioStream *as;
- for(ad=nil, as=devs, ae=as+ndevs; as<ae; as++)
- if(strcmp(as->name, "/dev/audio") == 0){- ad = as;
- break;
- }
+ threadsetname("arproc");as = arg;
- i = 0;
+ p = as->buf;
+ e = as->buf + as->bufsz;
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(as->paused){+ qlock(as);
+ rsleep(as);
+ qunlock(as);
}
+ if(as->closing)
+ break;
+ n = e - p < as->framesz ? e - p : as->framesz;
+ if((n = read(as->fd, p, n)) < 0)
+ break;
if(n == 0)
continue;
+ p += n;
+ if(p >= e)
+ p = as->buf;
qlock(as);
- if(as->fd < 0){- qunlock(as);
- break;
+ m = n + agetl(&as->avail);
+ if(m > as->bufsz){+ t = as->tail + n;
+ if(t >= as->bufsz)
+ t -= as->bufsz;
+ as->tail = t;
+ m = as->bufsz;
}
- 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;
+ aswapl(&as->avail, m);
qunlock(as);
+ /* FIXME: once queue fills up, this is bullshit, so this won't work */
+ if(as->fn != nil)
+ nbsend(ach, &n);
}
qlock(as);
- chanfree(as->pausec);
- chanfree(as->c);
- as->pausec = as->c = nil;
- free(as->buf);
- as->buf = nil;
+ close(as->fd);
+ as->fd = -1;
+ rwakeupall(as);
qunlock(as);
}
@@ -144,33 +188,66 @@
static void
awproc(void *arg)
{- int i, n;
- uchar *p;
+ int n, m, k;
+ uchar *s, *p, *e;
SDL_AudioStream *as;
+ threadsetname("awproc");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;
+ m = as->framesz;
+ p = as->buf;
+ e = as->buf + as->bufsz;
+ for(; !as->closing;){+ qlock(as);
+ rwakeupall(as);
+ rsleep(as);
+ qunlock(as);
+ n = agetl(&as->avail);
+ if(as->closing)
+ m = n;
+ while(n >= m){+ if(as->conv != nil){+ if((k = pcmconv(as->conv, p, as->outbuf, m)) <= 0)
+ continue;
+ s = as->outbuf;
+ }else{+ k = m;
+ s = p;
+ }
+ if(write(as->fd, s, k) != k){+ if(!as->closing)
+ fprint(2, "awproc: %r\n");
+ goto end;
+ }
+ n = aincl(&as->avail, -m);
+ p += m;
+ if(p >= e)
+ p = as->buf;
+ qlock(as);
+ rwakeup(as);
+ qunlock(as);
}
}
+end:
+ close(as->fd);
+ as->fd = -1;
qlock(as);
- chanfree(as->pausec);
- chanfree(as->c);
- as->pausec = as->c = nil;
- free(as->buf);
- as->buf = nil;
+ rwakeupall(as);
qunlock(as);
}
+void
+SDL_FreeAudioStream(SDL_AudioStream *as)
+{+ if(as->fd < 0)
+ return;
+ as->closing = 1;
+ as->paused = 0;
+ qlock(as);
+ rwakeupall(as);
+ qunlock(as);
+}
+
/* FIXME: bullshit */
static void
acbproc(void *arg)
@@ -179,6 +256,7 @@
SDL_AudioStream *as, *ae;
Channel *c;
+ threadsetname("acbproc");c = arg;
for(;;){if(recv(c, &n) < 0)
@@ -185,7 +263,7 @@
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);
+ as->fn(as->aux, as, n, agetl(&as->avail));
}
chanfree(c);
}
@@ -192,11 +270,15 @@
SDL_AudioStream* SDL_NewAudioStream(SDL_AudioFormat s, Uint8 sch, int srate, SDL_AudioFormat d, Uint8 dch, int drate)
{+ int n;
SDL_AudioDeviceID id;
SDL_AudioStream *as;
+ char *af;
+ Pcmdesc i, o;
/* FIXME */
id = SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK;
+ audiostreaming = 1;
if(--id < 0 || id >= ndevs){ werrstr("invalid device id %d", id);return nil;
@@ -210,58 +292,61 @@
}
if((as->fd = open(as->name, as->type)) < 0)
return nil;
+ as->closing = as->paused = 0;
+ as->outbuf = nil;
+ free(as->buf);
+ free(as->outbuf);
+ as->fn = nil;
as->aux = nil;
as->tail = 0;
- if((as->buf = mallocz(as->nframes * as->framesz, 1)) == nil)
+ as->avail.v = 0;
+ as->conv = nil;
+ /* FIXME: writers only, dest ignored; we force a destination output */
+ if(sch != as->channels || srate != as->freq || s != as->format){+ if(s >= nelem(fmts))
+ goto Err;
+ if((af = smprint("%sr%dc%d", fmts[s].spec, srate, sch)) == nil)+ goto Err;
+ n = mkpcmdesc(af, &i);
+ free(af);
+ if(n < 0)
+ goto Err;
+ USED(d, dch, drate); /* ignored */
+ if((af = smprint("%sr%dc%d", fmts[as->format].spec, as->freq, as->channels)) == nil)+ goto Err;
+ n = mkpcmdesc(af, &o);
+ free(af);
+ if(n < 0)
+ goto Err;
+ if((as->conv = allocpcmconv(&i, &o)) == nil)
+ goto Err;
+ n = srate / (1000 / Delayms);
+ as->framesz = n * fmts[s].ssz * sch;
+ /* FIXME: make sure we don't overflow, or add counterpart
+ * to pcmratio in libpcm */
+ n = as->freq / (1000 / Delayms);
+ n *= fmts[as->format].ssz * as->channels;
+ as->outsz = n;
+ if((as->outbuf = mallocz(n, 1)) == nil)
+ goto Err;
+ }else{+ as->framesz = as->delay * 2 * as->channels;
+ }
+ as->bufsz = as->nframes * as->framesz;
+ if(as->buf == nil && (as->buf = mallocz(as->bufsz, 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;
- }
+ fprint(2, "SDL_NewAudioStream: %r");
close(as->fd);
as->fd = -1;
free(as->buf);
+ as->buf = nil;
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)
{@@ -268,7 +353,7 @@
int i, n, dfd, type;
ulong len;
char *name, dir[] = "/dev/";
- SDL_AudioStream as;
+ SDL_AudioStream *as;
Dir *d;
if((dfd = open(dir, OREAD)) < 0)
@@ -307,23 +392,23 @@
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;
+ as = devs + ndevs++;
+ memset(as, 0, sizeof *as);
+ as->Rendez.l = &as->QLock;
+ as->fd = -1;
+ as->type = type;
+ as->name = name;
+ /* FIXME: read */
+ as->freq = 44100;
+ as->channels = 2;
+ as->format = AUDIO_S16LSB;
+ as->delay = as->freq / (1000 / Delayms);
+ as->nframes = Nframes;
}
free(d);
}
@@ -340,7 +425,8 @@
{SDL_AudioStream *as, *ae;
- chanclose(ach);
+ if(ach != nil)
+ chanclose(ach);
for(as=devs, ae=as+ndevs; as<ae; as++)
SDL_FreeAudioStream(as);
}
--
⑨