ref: ab547d8e97c1bbedfff2fe7d5ff18fc3171abc6d
dir: /libnpe_sdl2/audiostm.c/
#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;
}