diff --git a/kern/devaudio-sun.c b/kern/devaudio-sun.c new file mode 100644 index 0000000..d31206e --- /dev/null +++ b/kern/devaudio-sun.c @@ -0,0 +1,268 @@ +/* + * Sun + */ +#include +#include +#include "u.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "devaudio.h" + +enum +{ + Channels = 2, + Rate = 44100, + Bits = 16, +}; + +static char* afn = 0; +static char* cfn = 0; +static int afd = -1; +static int cfd = -1; +static int speed = Rate; +static int needswap = -1; + +static void +audiodevinit(void) +{ + uchar *p; + ushort leorder; + + if ((afn = getenv("AUDIODEV")) == nil) + afn = "/dev/audio"; + cfn = (char*)malloc(strlen(afn) + 3 + 1); + if(cfn == nil) + panic("out of memory"); + strcpy(cfn, afn); + strcat(cfn, "ctl"); + + /* + * Plan 9 /dev/audio is always little endian; + * solaris /dev/audio seems to expect native byte order, + * so on big endian machine (like sparc) we have to swap. + */ + leorder = (ushort) 0x0100; + p = (uchar*)&leorder; + if (p[0] == 0 && p[1] == 1) { + /* little-endian: nothing to do */ + needswap = 0; + } else { + /* big-endian: translate Plan 9 little-endian */ + needswap = 1; + } +} + +/* maybe this should return -1 instead of sysfatal */ +void +audiodevopen(void) +{ + audio_info_t info; + struct audio_device ad; + + if (afn == nil || cfn == nil) + audiodevinit(); + if((afd = open(afn, O_WRONLY)) < 0) + goto err; + if(cfd < 0 && (cfd = open(cfn, O_RDWR)) < 0) + goto err; + + AUDIO_INITINFO(&info); + info.play.precision = Bits; + info.play.channels = Channels; + info.play.sample_rate = speed; + info.play.encoding = AUDIO_ENCODING_LINEAR; + if(ioctl(afd, AUDIO_SETINFO, &info) < 0) + goto err; + + return; + +err: + if(afd >= 0) + close(afd); + afd = -1; + if(cfd >= 0) + close(cfd); + cfd = -1; + oserror(); +} + +void +audiodevclose(void) +{ + if(afd >= 0) + close(afd); + if(cfd >= 0) + close(cfd); + afd = -1; + cfd = -1; +} + +static double +fromsun(double val, double min, double max) +{ + return (val-min) / (max-min); +} + +static double +tosun(double val, double min, double max) +{ + return val*(max-min) + min; +} + +static void +setvolbal(double left, double right) +{ + audio_info_t info; + double vol, bal; + + if (left < 0 || right < 0) { + /* should not happen */ + return; + } else if (left == right) { + vol = tosun(left/100.0, AUDIO_MIN_GAIN, AUDIO_MAX_GAIN); + bal = AUDIO_MID_BALANCE; + } else if (left < right) { + vol = tosun(right/100.0, AUDIO_MIN_GAIN, AUDIO_MAX_GAIN); + bal = tosun(1.0 - left/right, AUDIO_MID_BALANCE, AUDIO_RIGHT_BALANCE); + } else if (right < left) { + vol = tosun(left/100.0, AUDIO_MIN_GAIN, AUDIO_MAX_GAIN); + bal = tosun(1.0 - right/left, AUDIO_MID_BALANCE, AUDIO_LEFT_BALANCE); + } + AUDIO_INITINFO(&info); + info.play.gain = (long)(vol+0.5); + info.play.balance = (long)(bal+0.5); + if(ioctl(cfd, AUDIO_SETINFO, &info) < 0) + oserror(); +} + +static void +getvolbal(int *left, int *right) +{ + audio_info_t info; + double gain, bal, vol, l, r; + + AUDIO_INITINFO(&info); + if (ioctl(cfd, AUDIO_GETINFO, &info) < 0) + oserror(); + + gain = info.play.gain; + bal = info.play.balance; + vol = fromsun(gain, AUDIO_MIN_GAIN, AUDIO_MAX_GAIN) * 100.0; + + if (bal == AUDIO_MID_BALANCE) { + l = r = vol; + } else if (bal < AUDIO_MID_BALANCE) { + l = vol; + r = vol * (1.0 - fromsun(bal, AUDIO_MID_BALANCE, AUDIO_LEFT_BALANCE)); + } else { + r = vol; + l = vol * (1.0 - fromsun(bal, AUDIO_MID_BALANCE, AUDIO_RIGHT_BALANCE)); + } + *left = (long)(l+0.5); + *right = (long)(r+0.5); + return; +} + +void +audiodevsetvol(int what, int left, int right) +{ + audio_info_t info; + ulong x; + int l, r; + + if (afn == nil || cfn == nil) + audiodevinit(); + if(cfd < 0 && (cfd = open(cfn, O_RDWR)) < 0) { + cfd = -1; + oserror(); + } + + if(what == Vspeed){ + x = left; + AUDIO_INITINFO(&info); + info.play.sample_rate = x; + if(ioctl(cfd, AUDIO_SETINFO, &info) < 0) + oserror(); + speed = x; + return; + } + if(what == Vaudio){ + getvolbal(&l, &r); + if (left < 0) + setvolbal(l, right); + else if (right < 0) + setvolbal(left, r); + else + setvolbal(left, right); + return; + } +} + +void +audiodevgetvol(int what, int *left, int *right) +{ + audio_info_t info; + + if (afn == nil || cfn == nil) + audiodevinit(); + if(cfd < 0 && (cfd = open(cfn, O_RDWR)) < 0) { + cfd = -1; + oserror(); + } + switch(what) { + case Vspeed: + *left = *right = speed; + break; + case Vaudio: + getvolbal(left, right); + break; + case Vtreb: + case Vbass: + *left = *right = 50; + break; + default: + *left = *right = 0; + } +} + + +static uchar *buf = 0; +static int nbuf = 0; + +int +audiodevwrite(void *v, int n) +{ + int i, m, tot; + uchar *p; + + if (needswap) { + if (nbuf < n) { + buf = (uchar*)erealloc(buf, n); + if(buf == nil) + panic("out of memory"); + nbuf = n; + } + + p = (uchar*)v; + for(i=0; i+1