ref: 353f9089e69e48fb66767b725cce6b438cb40668
parent: 964d0bdc5761cc09733b34b7bf65851eab3cd7cd
author: Russ Cox <rsc@swtch.com>
date: Mon May 29 11:24:15 EDT 2006
from axel belinfante
--- /dev/null
+++ b/kern/devaudio-sun.c
@@ -1,0 +1,268 @@
+/*
+ * Sun
+ */
+#include <sys/ioctl.h>
+#include <sys/audio.h>
+#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<n; i+=2) {
+ buf[i] = p[i+1];
+ buf[i+1] = p[i];
+ }
+ p = buf;
+ } else
+ p = (uchar*)v;
+
+ for(tot=0; tot<n; tot+=m)
+ if((m = write(afd, p+tot, n-tot)) <= 0)
+ oserror();
+ return tot;
+}
+
+int
+audiodevread(void *v, int n)
+{
+ error("no reading");
+ return -1;
+}