shithub: npe

ref: 623f5427f2e5d9cbc7540e8e2a72981c973e4cac
dir: /libnpe_sdl2/audio.c/

View raw version
#include <SDL2/SDL.h>

enum {
	Aout = 2,
	Arec,

	Audiobufsz = 8192,
};

typedef struct Audiodev Audiodev;

struct Audiodev {
	QLock;
	void (*cb)(void *, Uint8 *, int);
	void *userdata;
	char *name;
	Channel *wait;
	int paused;
	int fd;
	int pid;
	int mode;
	Uint8 buf[Audiobufsz];
};

/* FIXME extra USB audio devices? */
static Audiodev au[4] = {
	[0] = {.fd = -1, .mode = -1},
	[1] = {.fd = -1, .mode = -1},
	[Aout] = {.name = "/dev/audio", .fd = -1, .pid = -1, .mode = OWRITE},
	[Arec] = {.name = "/dev/audio", .fd = -1, .pid = -1, .mode = OREAD},
};

int
SDL_GetNumAudioDevices(int iscapture)
{
	/* FIXME look for extra USB devices? */
	USED(iscapture);
	return 1;
}

char *
SDL_GetAudioDeviceName(int index, int iscapture)
{
	/* FIXME look for extra USB devices? */
	USED(index);
	return au[iscapture ? Arec : Aout].name;
}

void
SDL_LockAudioDevice(SDL_AudioDeviceID id)
{
	qlock(&au[id]);
}

void
SDL_UnlockAudioDevice(SDL_AudioDeviceID id)
{
	qunlock(&au[id]);
}

static void
audiothread(void *p)
{
	Audiodev *a;

	a = p;
	threadsetname("%s (%s)", a->name, a->mode == OREAD ? "out" : "in");

	for(;;){
		qlock(a);
		if(a->paused)
			memset(a->buf, 0, sizeof(a->buf));
		else
			a->cb(a->userdata, a->buf, sizeof(a->buf));
		qunlock(a);

		if(write(a->fd, a->buf, sizeof(a->buf)) != sizeof(a->buf))
			break;
	}

	sendul(a->wait, 0);

	threadexits(nil);
}

void
SDL_PauseAudioDevice(SDL_AudioDeviceID id, SDL_bool pause)
{
	Audiodev *a;

	a = &au[id];
	if(a->paused && !pause){
		if(a->pid < 0)
			a->pid = proccreate(audiothread, a, mainstacksize);
		a->paused = 0;
	}else if(!a->paused && pause){
		a->paused = 1;
	}
}

SDL_AudioDeviceID
SDL_OpenAudioDevice(char *dev, int rec, SDL_AudioSpec *want, SDL_AudioSpec *have, u32int change)
{
	Audiodev *a;
	SDL_AudioDeviceID id;

	/* FIXME look for extra USB devices? */
	USED(dev);

	if(change != SDL_AUDIO_ALLOW_ANY_CHANGE){ /* FIXME sampling in mono */
		werrstr("SDL_OpenAudioDevice: changes not implemented");
		return 0;
	}

	have->freq = 44100;
	have->format = AUDIO_S16;
	have->channels = 2;
	have->samples = want->samples;

	id = rec ? Arec : Aout;
	a = &au[id];

	if(a->fd < 0 && (a->fd = open("/dev/audio", a->mode|OCEXEC)) < 0){
		werrstr("SDL_OpenAudioDevice: %r");
		return 0;
	}

	a->userdata = want->userdata;
	a->cb = want->callback;
	a->paused = 1;
	a->wait = chancreate(sizeof(ulong), 0);

	return id;
}

void
SDL_CloseAudioDevice(SDL_AudioDeviceID id)
{
	Audiodev *a;

	a = &au[id];

	qlock(a);
	close(a->fd);
	a->fd = -1;
	a->pid = -1;
	a->userdata = nil;
	a->cb = nil;
	qunlock(a);

	recvul(a->wait);
	chanfree(a->wait);
}