shithub: qk2

ref: 4c66d61f63d2ca97101d7aad96c6fb412fbc6c4b
dir: /cd.c/

View raw version
#include <u.h>
#include <libc.h>
#include <stdio.h>
#include <thread.h>
#include <regexp.h>
#include "dat.h"
#include "fns.h"

static int cdread, cdloop;
static double cdvol;
static int ntrk, trk, chtrk;
static char trtype;
static int cpid = -1;
static cvar_t *ccdvol;

static int
cdinfo(void)
{
	int fd, i, n;
	Dir *d;
	Reprog *pat;

	ntrk = 0;

	if((fd = open("/mnt/cd", OREAD)) < 0)
		return -1;
	n = dirreadall(fd, &d);
	close(fd);
	if(n < 0)
		return -1;
	pat = regcomp("[au][0-9][0-9][0-9]");
	for(i = 0; i < n; i++)
		if(regexec(pat, d[i].name, nil, 0)){
			if(!trtype)
				trtype = d[i].name[0];
			ntrk++;
		}
	free(pat);
	free(d);
	if(ntrk < 1)
		return -1;
	return 0;
}

static void
cproc(void *)
{
	int a, n, afd, fd;
	char s[24];
	uchar buf[Nsbuf];
	short *p, *e;

	if((afd = open("/dev/audio", OWRITE)) < 0){
		fprint(2, "cd: open: %r\n");
		return;
	}
	fd = -1;
	for(;;){
		if(chtrk > 0){
			close(fd);
			trk = chtrk;
			snprint(s, sizeof s, "/mnt/cd/%c%03ud", trtype, trk);
			if((fd = open(s, OREAD)) < 0)
				fprint(2, "cd: open: %r\n");
			chtrk = 0;
		}
		if(!cdread || fd < 0){
			sleep(1);
			continue;
		}
		if((n = read(fd, buf, sizeof buf)) < 0)
			break;
		if(n == 0){
			if(cdloop)
				seek(fd, 0, 0);
			else{
				close(fd);
				fd = -1;
			}
			continue;
		}
		p = (short *)buf;
		e = (short *)(buf + n);
		while(p < e){
			a = *p * cdvol;
			*p++ = a;
		}
		if(write(afd, buf, n) != n)
			break;
		/* threadkill requires that a libthread function be used
		 * somewhere: thread(2), errors notes and resources */
		yield();
	}
}

void
CDAudio_Play(int nt, qboolean loop)
{
	if(cpid < 0)
		return;
	nt -= 1;	/* d001 was assumed part of the track list */
	if(nt < 1 || nt > ntrk){
		Com_Printf("cd: invalid track number %d\n", nt);
		return;
	}

	chtrk = nt;
	cdloop = loop;
	cdvol = ccdvol->value;
	if(cdvol > 0)
		cdread = 1;
}

void
CDAudio_Stop(void)
{
	cdread = 0;
	cdloop = 0;
}

void
CDAudio_Pause(void)
{
	cdread = 0;
}

void
CDAudio_Resume(void)
{
	cdread = 1;
}

void
CD_f(void)
{
	char *cmd;

	if(Cmd_Argc() < 2){
usage:
		Com_Printf("cd (play|loop|stop|pause|resume|info) [track]\n");
		return;
	}
	cmd = Cmd_Argv(1);
	if(cistrcmp(cmd, "play") == 0){
		if(Cmd_Argc() < 3)
			goto usage;
		CDAudio_Play(atoi(Cmd_Argv(2)), false);
	}else if(cistrcmp(cmd, "loop") == 0){
		if(Cmd_Argc() < 3)
			goto usage;
		CDAudio_Play(atoi(Cmd_Argv(2)), true);
	}else if(cistrcmp(cmd, "stop") == 0)
		CDAudio_Stop();
	else if(cistrcmp(cmd, "pause") == 0)
		CDAudio_Pause();
	else if(cistrcmp(cmd, "resume") == 0)
		CDAudio_Resume();
	else if(cistrcmp(cmd, "info") == 0)
		Com_Printf("track %d/%d; loop %d; vol %.1f\n", trk, ntrk, cdloop, cdvol);
	else
		goto usage;
}

void
CDAudio_Update(void)
{
	if(cpid < 0)
		return;
	if(ccdvol->value <= 0.0 || cdread == 0)
		return;
	cdvol = ccdvol->value;
	cdread = cdvol > 0.0;
}

int
CDAudio_Init(void)
{
	int tid;

	if(cdinfo() < 0)
		return -1;
	ccdvol = Cvar_Get("cdvol", "1", CVAR_ARCHIVE);
	if((tid = proccreate(cproc, nil, 16384)) < 0)
		sysfatal("proccreate: %r");
	cpid = threadpid(tid);
	Cmd_AddCommand("cd", CD_f);

	Com_Printf("CD Audio Initialized\n");
	return 0;
}

void
CDAudio_Shutdown(void)
{
	if(cpid < 0)
		return;

	CDAudio_Stop();
	if(postnote(PNPROC, cpid, "die die die") < 0)
		fprint(2, "CDAudio_Shutdown: postnote: %r\n");
	cpid = -1;
	cdread = cdloop = 0;
}