diff options
Diffstat (limited to 'emu/Hp/devaudio.c')
| -rw-r--r-- | emu/Hp/devaudio.c | 506 |
1 files changed, 506 insertions, 0 deletions
diff --git a/emu/Hp/devaudio.c b/emu/Hp/devaudio.c new file mode 100644 index 00000000..c26143e3 --- /dev/null +++ b/emu/Hp/devaudio.c @@ -0,0 +1,506 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" + +#include <sys/audio.h> + + +enum{ + Qdir, + Qaudio, + Qaudioctl, /* deprecated */ + Qvolume, + + Fmono = 1, + Fin = 2, + Fout = 4, + Fhearn = 8, + + Vaudio = 0, + Vsynth, + Vcd, + Vline, + Vmic, + Vspeaker, + Vtreb, + Vbass, + Vspeed, + Vbits, + Vchans, + Venc, + Nvol, + + Epcm = 0, + Eulaw, + Ealaw, + Nenc, + + Speed = 44100, + Ncmd = 50, /* max volume command words */ +}; +Dirtab audiotab[]={ + "audio", {Qaudio, 0}, 0, 0666, + "audioctl", {Qaudioctl, 0}, 0, 0666, + "volume", {Qvolume, 0}, 0, 0666, +}; + +typedef struct Audiodev Audiodev; + +struct Audiodev { + int fd; + int swab; + int repl1; + int repl2; +}; + +static struct +{ + QLock; /* XXX maybe we should use this guy! */ + int rivol[Nvol]; /* right/left input/output volumes */ + int livol[Nvol]; + int rovol[Nvol]; + int lovol[Nvol]; +} audio; + +static struct +{ + char* name; + int flag; + int ilval; /* initial values */ + int irval; +} volumes[] = +{ +/*[Vaudio]*/ "audio", Fout, 50, 50, +/*[Vsynth]*/ "synth", Fin|Fout, 0, 0, +/*[Vcd]*/ "cd", Fin|Fout, 0, 0, +/*[Vline]*/ "line", Fin|Fout, 0, 0, +/*[Vmic]*/ "mic", Fin|Fout|Fmono, 0, 0, +/*[Vspeaker]*/ "speaker", Fout|Fmono, 0, 0, + +/*[Vtreb]*/ "treb", Fout, 50, 50, +/*[Vbass]*/ "bass", Fout, 50, 50, + +/*[Vspeed]*/ "speed", Fin|Fout|Fmono, Speed, Speed, +/*[Vbits]*/ "bits", Fin|Fout|Fmono|Fhearn, 16, 16, +/*[Vchans]*/ "chans", Fin|Fout|Fmono|Fhearn, 2, 2, +/*[Venc]*/ "enc", Fin|Fout|Fmono|Fhearn, Epcm, Epcm, + 0 +}; + +static char *encname[] = +{ +/*Epcm*/ "pcm", +/*Eulaw*/ "ulaw", +/*Ealaw*/ "alaw", +}; + +static char Evolume[] = "illegal volume specifier"; + +static void +resetlevel(void) +{ + int i; + + for(i=0; volumes[i].name; i++) { + audio.lovol[i] = volumes[i].ilval; + audio.rovol[i] = volumes[i].irval; + audio.livol[i] = volumes[i].ilval; + audio.rivol[i] = volumes[i].irval; + } +} + +/* Start OS-dependant code */ +static int +doioctl(int fd, int whim, void *data, char *what) +{ + char ebuf[ERRMAX]; + int r, n; + + osenter(); + r = ioctl(fd, whim, data); + osleave(); + if (r < 0) { + n = snprint(ebuf, ERRMAX, "ioctl %s: ", what); + oserrstr(ebuf+n, sizeof ebuf-n); + error(ebuf); + } + return r; +} + +static void +setlevels(Audiodev *a) +{ + struct audio_describe au; + struct audio_gain gain; + int i, x; + +/* XXX todo: simulate it with a data conversion routine (could also do swab...) */ + if (audio.lovol[Venc] == Epcm && audio.lovol[Vbits] != 16) { + audio.lovol[Vbits] = 16; + error("pcm must be 16 bits"); + } + if (audio.lovol[Vchans] != 1 && audio.lovol[Vchans] != 2) { + audio.lovol[Vchans] = 1; + error("bad number of channels"); + } + + doioctl(a->fd, AUDIO_DESCRIBE, &au, "describe"); + doioctl(a->fd, AUDIO_SET_SAMPLE_RATE, audio.lovol[Vspeed], "rate"); /* what if input != output??? */ + doioctl(a->fd, AUDIO_SET_CHANNELS, audio.lovol[Vchans], "channels"); + + switch (audio.lovol[Venc]) { + default: + case Epcm: + x = AUDIO_FORMAT_LINEAR16BIT; + break; + case Eulaw: + x = AUDIO_FORMAT_ULAW; + break; + case Ealaw: + x = AUDIO_FORMAT_ALAW; + break; + } + doioctl(a->fd, AUDIO_SET_DATA_FORMAT, x, "set format"); + + x = 0; + if (audio.lovol[Vspeaker] != 0 || audio.rovol[Vspeaker] != 0) + x |= AUDIO_OUT_SPEAKER; + if (audio.lovol[Vaudio] != 0 || audio.rovol[Vaudio] != 0) + x |= AUDIO_OUT_HEADPHONE; + if (audio.lovol[Vline] != 0 || audio.rovol[Vline] != 0) + x |= AUDIO_OUT_LINE; + doioctl(a->fd, AUDIO_SET_OUTPUT, x, "set output"); + + x = 0; + if (audio.livol[Vline] != 0 || audio.rivol[Vline] != 0) + x |= AUDIO_IN_LINE; + if (audio.livol[Vmic] != 0 || audio.rivol[Vmic] != 0 || x == 0) /* must set at least one */ + x |= AUDIO_IN_MIKE; + doioctl(a->fd, AUDIO_SET_INPUT, x, "set input"); + +/* XXX todo: get the gains right. should scale 0-100 into min-max (as in struct audio_describe au) */ +/* doioctl(a->fd, AUDIO_GET_GAINS, &gain, "get gains"); */ + gain.channel_mask = AUDIO_CHANNEL_LEFT|AUDIO_CHANNEL_RIGHT; + for (i = 0; i < 2; i++) { + gain.cgain[i].receive_gain = au.min_receive_gain; + gain.cgain[i].monitor_gain = au.min_monitor_gain; + gain.cgain[i].transmit_gain = au.max_transmit_gain; + } + doioctl(a->fd, AUDIO_SET_GAINS, &gain, "set gains"); +} + +static char * +audiofname(int isctl) +{ + if (isctl) + return "/dev/audioCtl"; + else + return "/dev/audio"; +} + +static void +audioswab(uchar *p, int n) +{ + int x; + + /* XXX slow; should check for 16bit mode; should be combined with format conversion; etc */ + while (n >= 2) { + x = p[0]; + p[0] = p[1]; + p[1] = x; + p +=2; + n -=2; + } +} +/* End OS-dependant code */ + +static void +audioinit(void) +{ + resetlevel(); +} + +static Chan* +audioattach(char* spec) +{ + return devattach('A', spec); +} + +static int +audiowalk(Chan* c, char* name) +{ + return devwalk(c, name, audiotab, nelem(audiotab), devgen); +} + +static void +audiostat(Chan* c, char* db) +{ + devstat(c, db, audiotab, nelem(audiotab), devgen); +} + +static Chan* +audioopen(Chan *c, int omode) +{ + Audiodev *a; + long path; + char ebuf[ERRMAX]; + + path = c->qid.path & ~CHDIR; + if (path != Qdir){ +/* XXX Irix portability? (multiple opens -- how to match ctl with data???) */ + a = malloc(sizeof(Audiodev)); + if(a == nil) + error(Enomem); + if (waserror()) { + free(a); + nexterror(); + } + a->fd = open(audiofname(path != Qaudio), omode&7); + if(a->fd < 0) + oserror(); + if (path == Qaudio) + setlevels(a); + c->aux = a; + poperror(); + } + return devopen(c, omode, audiotab, nelem(audiotab), devgen); +} + +static void +audioclose(Chan* c) +{ + Audiodev *a; + + a = c->aux; + if (a != nil) { + close(a->fd); + free(a); + } +} + +static long +audioread(Chan* c, void *ua, long n, vlong offset) +{ + Audiodev *a; + char buf[300], ebuf[ERRMAX]; + int liv, riv, lov, rov; + int j, m; + long path; + + a = c->aux; + path = (c->qid.path & ~CHDIR); + switch(path){ + case Qdir: + return devdirread(c, a, n, audiotab, nelem(audiotab), devgen); + case Qaudio: + osenter(); + n = read(a->fd, ua, n); + osleave(); + if (n < 0) + oserror(); + audioswab(ua, n); /* XXX what if n is odd? also, only if 16 bit... must fix portability */ + break; + case Qaudioctl: + case Qvolume: + j = 0; + buf[0] = 0; + for(m=0; volumes[m].name; m++){ + if ((volumes[m].flag & Fhearn) && path == Qvolume) + continue; + liv = audio.livol[m]; + riv = audio.rivol[m]; + lov = audio.lovol[m]; + rov = audio.rovol[m]; + j += snprint(buf+j, sizeof(buf)-j, "%s", volumes[m].name); + if(m == Venc) + j += snprint(buf+j, sizeof(buf)-j, " %s", encname[lov]); + else if((volumes[m].flag & Fmono) || liv==riv && lov==rov){ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov) + j += snprint(buf+j, sizeof(buf)-j, " %d", liv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, " in %d", liv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, " out %d", lov); + } + }else{ + if((volumes[m].flag&(Fin|Fout))==(Fin|Fout) && liv==lov && riv==rov) + j += snprint(buf+j, sizeof(buf)-j, " left %d right %d", + liv, riv); + else{ + if(volumes[m].flag & Fin) + j += snprint(buf+j, sizeof(buf)-j, " in left %d right %d", + liv, riv); + if(volumes[m].flag & Fout) + j += snprint(buf+j, sizeof(buf)-j, " out left %d right %d", + lov, rov); + } + } + j += snprint(buf+j, sizeof(buf)-j, "\n"); + } + return readstr(offset, ua, n, buf); + default: + n=0; + break; + } + return n; +} + +static long +audiowrite(Chan* c, char *ua, long n, vlong offset) +{ + Audiodev *a; + long m, n0; + int i, nf, v, left, right, in, out; + char buf[255], *field[Ncmd], ebuf[ERRMAX], *p; + + a = c->aux; + switch(c->qid.path & ~CHDIR){ + case Qaudio: + n &= ~1; + audioswab(ua, n); /* XXX VERY BAD BUG; THIS CHANGES THE CALLER'S DATA */ + osenter(); + n = write(a->fd, ua, n); + osleave(); + if (n < 0) + oserror(); + break; + case Qaudioctl: + case Qvolume: + v = Vaudio; + left = 1; + right = 1; + in = 1; + out = 1; + if(n > sizeof(buf)-1) + n = sizeof(buf)-1; + memmove(buf, ua, n); + buf[n] = '\0'; + + nf = getfields(buf, field, Ncmd, 1, " \t\n"); + for(i = 0; i < nf; i++){ + /* + * a number is volume + */ + if(field[i][0] >= '0' && field[i][0] <= '9') { + m = strtoul(field[i], &p, 10); + if (p != nil && *p == 'k') + m *= 1000; + if(left && out) + audio.lovol[v] = m; + if(left && in) + audio.livol[v] = m; + if(right && out) + audio.rovol[v] = m; + if(right && in) + audio.rivol[v] = m; + setlevels(a); + goto cont0; + } + + for(m=0; volumes[m].name; m++) { + if(strcmp(field[i], volumes[m].name) == 0) { + v = m; + in = 1; + out = 1; + left = 1; + right = 1; + goto cont0; + } + } + + if(strcmp(field[i], "reset") == 0) { + resetlevel(); + setlevels(a); + goto cont0; + } + if(strcmp(field[i], "in") == 0) { + in = 1; + out = 0; + goto cont0; + } + if(strcmp(field[i], "out") == 0) { + in = 0; + out = 1; + goto cont0; + } + if(strcmp(field[i], "left") == 0) { + left = 1; + right = 0; + goto cont0; + } + if(strcmp(field[i], "right") == 0) { + left = 0; + right = 1; + goto cont0; + } + if(strcmp(field[i], "rate") == 0) { + v = Vspeed; + in = 1; + out = 1; + left = 1; + right = 1; + goto cont0; + } + if(strcmp(field[i], "chan") == 0) { /* XXX egregious backward compatibility hack */ + v = Vchans; + in = 1; + out = 1; + left = 1; + right = 1; + goto cont0; + } + if(v == Venc) { + if (strcmp(field[i], "pcm") == 0) { + audio.lovol[v] = Epcm; + goto cont0; + } + if (strcmp(field[i], "ulaw") == 0) { + audio.lovol[v] = Eulaw; + goto cont0; + } + if (strcmp(field[i], "alaw") == 0) { + audio.lovol[v] = Ealaw; + goto cont0; + } + } + if(v == Vchans) { + if (strcmp(field[i], "mono") == 0) { + audio.lovol[v] = 1; + goto cont0; + } + if (strcmp(field[i], "stereo") == 0) { + audio.lovol[v] = 2; + goto cont0; + } + } + error(Evolume); + break; + cont0:; + } + break; + default: + error(Ebadusefd); + } + return n; +} + +Dev audiodevtab = { + 'A', + "audio", + + audioinit, + audioattach, + devclone, + audiowalk, + audiostat, + audioopen, + devcreate, + audioclose, + audioread, + devbread, + audiowrite, + devbwrite, + devremove, + devwstat, +}; |
