Plan 9 from Bell Labs’s /sys/src/games/mixer.c

Copyright © 2021 Plan 9 Foundation
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>

Font *font;

enum {
	Master,
	Treble,
	Bass,
	CD,
	Line,
	Synth,
	Mic,
	Speaker,
	Nvolume,

	Unknown = 0,
	Usb,
	SharpA,
};

typedef struct Volume	Volume;
struct Volume {
	char *name;
	char *usbname;
	char *desc;
	int llevel;
	int rlevel;
	int res;
	int min;
	int max;
	int active;
	Rectangle r;
	Image *label;
};

Volume voltab[Nvolume] = {
[Master]	{ "audio", "volume", "volume", },
[Treble]	{ "treble", "treb", "treble", },
[Bass]	{ "bass", "bass", "bass", },
[CD]		{ "cd", nil, "cd", 0, },
[Speaker]	{ "speaker", nil, "speaker", 0, },
[Synth]	{ "synth", nil, "synth", 0, },
[Mic]		{ "mic", nil, "mic", 0, },
[Line]	{ "line", nil, "line", 0 },
};

int type;
int vfd;
int boxwid;

void
getvol(void)
{
	char buf[4096];
	char *line[32], *f[32];
	int i, j, n, nf;
	Volume *v, *ev;

	seek(vfd, 0, 0);
	if((n = readn(vfd, buf, sizeof buf)) <= 0) {
		fprint(2, "cannot read volume file: %r\n");
		exits("read");
	}

	if(n >= sizeof buf)
		n = sizeof(buf)-1;
	buf[n] = 0;
	n = getfields(buf, line, nelem(line), 0, "\n");
	ev = voltab+nelem(voltab);
	for(i=0; i<n; i++){
		nf = tokenize(line[i], f, nelem(f));
		if(nf < 2)
			continue;
		for(v=voltab; v<ev; v++){
			if(v->name && strcmp(v->name, f[0])==0){
				if(!v->usbname || strcmp(v->usbname, v->name) != 0)
					type = SharpA;
				break;
			}
			if(v->usbname && strcmp(v->usbname, f[0])==0){
				type = Usb;
				break;
			}
		}

		if(v==ev)
{
			continue;
}

// #A:             source in left value right value out left value right value
// USB:           volume      out               -5376      -15616           0         256
		j = 1;
		if(strcmp(f[j], "in") == 0){
			while(j < nf && strcmp(f[j], "out") != 0)
				j++;
			j++;
		}else{
			if(strcmp(f[j], "out") == 0)
				j++;
		}
		if(j >= nf)
{
			continue;
}
		v->active = 1;
		if(strcmp(f[j], "left") == 0 && nf-j >= 4 && strcmp(f[j+2], "right") == 0){
			type = SharpA;
			v->llevel = atoi(f[j+1]);
			v->rlevel = atoi(f[j+3]);
			v->min = 0;
			v->max = 100;
			v->res = 1;
			continue;
		}
		if(j==2 && nf==6){
			type = Usb;
			v->llevel = v->rlevel = atoi(f[j]);
			v->min = atoi(f[j+1]);
			v->max = atoi(f[j+2]);
			v->res = atoi(f[j+3]);
			continue;
		}
		type = SharpA;
		v->llevel = v->rlevel = atoi(f[j]);
		v->min = 0;
		v->max = 100;
		v->res = 1;
	}
}

void
setvol(Volume *v)
{
	seek(vfd, 0, 0);
	switch(type){
	case SharpA:
		if(v->llevel==v->rlevel)
			fprint(vfd, "%s %d", v->name, v->llevel);
		else
			fprint(vfd, "%s left %d right %d", v->name, v->llevel, v->rlevel);
		break;
	case Usb:
		fprint(vfd, "%s '%d %d'", v->usbname, v->llevel, v->rlevel);
		break;
	default:
		fprint(2, "unknown mixer type");
		break;
	}
}

int nvrect;
int toprect;
Image *slidercolor;
Image *lightgrey;

void
drawvol(Volume *v, int new)
{
	int midly, midry, midx;
	Rectangle r;

	midly = v->r.max.y-(Dy(v->r)*(v->llevel-v->min))/(v->max - v->min);
	midry = v->r.max.y-(Dy(v->r)*(v->rlevel-v->min))/(v->max - v->min);
	midx = v->r.min.x+Dx(v->r)/2;

	if(new) {
		border(screen, v->r, -1, display->black, ZP);
		draw(screen, v->r, lightgrey, nil, ZP);
	}

	draw(screen, Rect(v->r.min.x, midly, midx, v->r.max.y), slidercolor, nil, ZP);
	draw(screen, Rect(midx+1, midry, v->r.max.x, v->r.max.y), slidercolor, nil, ZP);
//	line(screen, Pt(midx, v->r.max.y), Pt(midx, midly > midry ? midly : midry), 0, 0, 0, display->black, ZP);

	if(!new) {
		draw(screen, Rect(v->r.min.x, v->r.min.y, midx, midly), lightgrey, nil, ZP);
		draw(screen, Rect(midx+1, v->r.min.y, v->r.max.x, midry), lightgrey, nil, ZP);
	//	line(screen, Pt(midx, midly > midry ? midly : midry), Pt(midx, v->r.min.y), 0, 0, 0, lightgrey, ZP);
	}

	r = Rpt(subpt(v->label->r.min, v->label->r.max), ZP);
	draw(screen, rectaddpt(r, Pt(v->r.max.x-2, v->r.max.y-5)), display->black, v->label, ZP);
}
	
void
redraw(Image *screen)
{
	enum { PAD=3, MARGIN=5 };
	int i, ht;
	Point p;
	Volume *v;

	ht = Dy(screen->r)-2*MARGIN;
	draw(screen, screen->r, lightgrey, nil, ZP);
	p = addpt(screen->r.min, Pt(MARGIN, MARGIN));
	for(i=0; i<Nvolume; i++) {
		v = &voltab[i];
		if(!v->active){
			v->r = Rect(0,0,0,0);
			continue;
		}
		v->r = Rpt(p, addpt(p, Pt(boxwid, ht)));
		p.x += boxwid+PAD;
		drawvol(v, 1);
	}
}

void
click(Mouse m)
{
	Volume *v, *ev;
	int delta, lev, or, ol;
	int what;

	if(m.buttons == 0 || (m.buttons & ~2))
		return;

	for(v=voltab, ev=v+nelem(voltab); v<ev; v++) {
		if(ptinrect(m.xy, v->r))
			break;
	}
	if(v >= ev)
		return;

	delta = m.xy.x - v->r.min.x;
	if(delta < Dx(v->r)/3)
		what = 1;
	else if(delta < (2*Dx(v->r))/3)
		what = 3;
	else
		what = 2;

	or = v->rlevel;
	ol = v->llevel;
	do {
		lev = v->min+((v->r.max.y - m.xy.y)*(v->max-v->min))/Dy(v->r);
		if(lev < v->min)
			lev = v->min;
		if(lev > v->max)
			lev = v->max;
		if(what & 1)
			v->llevel = lev;
		if(what & 2)
			v->rlevel = lev;
		setvol(v);
		drawvol(v, 0);
		m = emouse();
	} while(m.buttons == 2);

	if(m.buttons != 0) {
		v->rlevel = or;
		v->llevel = ol;
		setvol(v);
		drawvol(v, 0);
		while(m.buttons)
			m = emouse();
	}
}

/*
 * you can do this with clever blts.
 * i don't care at the moment. 
 */
void
rotate90(Image **ip)
{
	int dx, bx, x, y, dy, by;
	uchar *s, *d;
	Image *i, *ni;
	Rectangle r;

	i = *ip;
	assert(i->depth==1);
	r = i->r;
	ni = allocimage(display, Rect(r.min.y, r.min.x, r.max.y, r.max.x), i->chan, i->repl, DNofill);
	if(ni == nil)
		sysfatal("rotate90 allocimage: %r");

	s = malloc(2*Dx(r)*Dy(r));
	if(s == nil)
		sysfatal("rotate90 malloc: %r");

	if(unloadimage(i, r, s, bytesperline(i->r, i->depth)*Dy(r)) != bytesperline(i->r, i->depth)*Dy(r))
		sysfatal("unloadimage: %r");

	d = s+Dx(r)*Dy(r);
	memset(d, 0, Dx(r)*Dy(r));
	dx = Dx(r);
	dy = Dy(r);
	bx = (dx+7)/8;
	by = (dy+7)/8;
	for(y=0; y<dy; y++)
	for(x=0; x<dx; x++)
		d[x*by+y/8] |= (s[y*bx+(dx-1-x)/8] & (1<<(7-(dx-1-x)%8))) ? (1<<(7-(y%8))) : 0;

	if(loadimage(ni, ni->r, d, bytesperline(ni->r, ni->depth)*Dy(ni->r)) != bytesperline(ni->r, ni->depth)*Dy(ni->r))
		sysfatal("loadimage: %r");

	freeimage(i);
	free(s);
	*ip = ni;
}

char *files[] = {
	"/dev/audioctl",
	"/dev/volume",
	"#A/volume",
};

void
main(int argc, char **argv)
{
	int i;
	Event e;
	Point p;

	ARGBEGIN{
	default:
		goto Usage;
	}ARGEND;

	switch(argc){
	case 0:
		vfd = -1;
		for(i=0; i<nelem(files); i++)
			if((vfd = open(files[i], ORDWR)) >= 0)
				break;
		if(vfd < 0)
			sysfatal("couldn't find volume file");
		break;
	case 1:
		if((vfd = open(argv[0], ORDWR)) < 0)
			sysfatal("open %s: %r", argv[0]);
		break;
	default:
	Usage:
		fprint(2, "usage: games/mixer [/dev/volume]\n");
		exits("usage");
	}

	getvol();
	initdraw(0, 0, "mixer");
	slidercolor = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DYellow);
	lightgrey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xCCCCCCFF);
	font = display->defaultfont;
	for(i=0; i<Nvolume; i++) {
		p = stringsize(font, voltab[i].desc);
		voltab[i].label = allocimage(display, Rpt(ZP, p), GREY1, 0, DBlack);
		string(voltab[i].label, ZP, display->white, ZP, font, voltab[i].desc);
		rotate90(&voltab[i].label);
	}
	p = stringsize(font, "0");
	boxwid = p.y + 2*2;
	redraw(screen);
	einit(Emouse|Ekeyboard);

	for(;;) {
		switch(eread(Emouse|Ekeyboard, &e)) {
		case Ekeyboard:
			if(e.kbdc == 0x7F || e.kbdc == 'q')
				exits(0);
			break;
		case Emouse:
			if(e.mouse.buttons)
				click(e.mouse);
			break;
		}
	}
}

void
eresized(int new)
{
	if(new && getwindow(display, Refmesg) < 0)
		fprint(2,"can't reattach to window");
	redraw(screen);
}


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@plan9.bell-labs.com.