summaryrefslogtreecommitdiff
path: root/emu/Solaris/audio.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2009-03-25 15:55:14 +0000
committerCharles.Forsyth <devnull@localhost>2009-03-25 15:55:14 +0000
commitdfd1934d5e1ddbeb326f77fc0e52307c801a1a3e (patch)
treef1e8b23278caae95e01d88b00421d6c3642357ef /emu/Solaris/audio.c
parent78dfdcbd59dc8f36975e7695933e3f753957474c (diff)
x20090325-1554
Diffstat (limited to 'emu/Solaris/audio.c')
-rw-r--r--emu/Solaris/audio.c593
1 files changed, 593 insertions, 0 deletions
diff --git a/emu/Solaris/audio.c b/emu/Solaris/audio.c
new file mode 100644
index 00000000..91ffd8d6
--- /dev/null
+++ b/emu/Solaris/audio.c
@@ -0,0 +1,593 @@
+#include "dat.h"
+#include "fns.h"
+#include "error.h"
+#define __EXTENSIONS__
+#include <sys/time.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stropts.h>
+#include <sys/audioio.h>
+#include <sys/ioctl.h>
+#include <sys/filio.h>
+#include "audio.h"
+#include <sys/audioio.h>
+
+#define Audio_Mic_Val AUDIO_MICROPHONE
+#define Audio_Linein_Val AUDIO_LINE_IN
+
+#define Audio_Speaker_Val AUDIO_SPEAKER
+#define Audio_Headphone_Val AUDIO_HEADPHONE
+#define Audio_Lineout_Val AUDIO_LINE_OUT
+
+#define Audio_Pcm_Val AUDIO_ENCODING_LINEAR
+#define Audio_Ulaw_Val AUDIO_ENCODING_ULAW
+#define Audio_Alaw_Val AUDIO_ENCODING_ALAW
+
+#include "audio-tbls.c"
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+static int debug = 0;
+
+extern int nanosleep(const struct timespec *, struct timespec *);
+
+#define AUDIO_FILE_STRING "/dev/audio"
+
+enum {
+ A_Pause,
+ A_UnPause
+};
+
+enum {
+ A_In,
+ A_Out
+};
+
+static QLock inlock;
+static QLock outlock;
+
+static int audio_file_in = -1; /* file in */
+static int audio_file_out = -1; /* file out */
+
+static int audio_swap_flag = 0; /* endian swap */
+
+static int audio_in_pause = A_UnPause;
+
+static Audio_t av;
+
+static int audio_enforce(Audio_t*);
+static int audio_open_in(void);
+static int audio_open_out(void);
+static int audio_pause_in(int, int);
+static int audio_flush(int, int);
+static int audio_pause_out(int);
+static int audio_set_blocking(int);
+static int audio_set_info(int, Audio_d*, int);
+static void audio_swap_endian(char*, int);
+
+void
+audio_file_init(void)
+{
+ static ushort flag = 1;
+ audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */
+ audio_info_init(&av);
+}
+
+void
+audio_file_open(Chan *c, int omode)
+{
+ switch(omode){
+ case OREAD:
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in >= 0)
+ error(Einuse);
+ if((audio_file_in = audio_open_in()) < 0)
+ oserror();
+
+ poperror();
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_out >= 0)
+ error(Einuse);
+ if((audio_file_out = audio_open_out() ) < 0)
+ oserror();
+ poperror();
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&inlock);
+ qunlock(&outlock);
+ nexterror();
+ }
+ if(audio_file_in >= 0 || audio_file_out >= 0)
+ error(Einuse);
+
+ if((audio_file_in = audio_open_in()) < 0)
+ oserror();
+ if(waserror()){
+ close(audio_file_in);
+ audio_file_in = -1;
+ nexterror();
+ }
+ if((audio_file_out = audio_open_out()) < 0)
+ oserror();
+ poperror();
+
+ poperror();
+ qunlock(&inlock);
+ qunlock(&outlock);
+ break;
+ }
+}
+
+void
+audio_file_close(Chan *c)
+{
+ switch(c->mode){
+ case OREAD:
+ qlock(&inlock);
+ close(audio_file_in);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ break;
+ case OWRITE:
+ qlock(&outlock);
+ close(audio_file_out);
+ audio_file_out = -1;
+ qunlock(&outlock);
+ break;
+ case ORDWR:
+ qlock(&inlock);
+ close(audio_file_in);
+ audio_file_in = -1;
+ qunlock(&inlock);
+ qlock(&outlock);
+ close(audio_file_out);
+ audio_file_out = -1;
+ qunlock(&outlock);
+ break;
+ }
+}
+
+long
+audio_file_read(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long ba, status, chunk, total;
+ char *pva = (char *) va;
+
+ qlock(&inlock);
+ if(waserror()){
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if(audio_file_in < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.in.bits * av.in.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(!audio_pause_in(audio_file_in, A_UnPause))
+ error(Eio);
+
+ total = 0;
+ while(total < count) {
+ chunk = count - total;
+ osenter();
+ status = read(audio_file_in, pva + total, chunk);
+ osleave();
+ if(status < 0)
+ error(Eio);
+ total += status;
+ }
+
+ if(total != count)
+ error(Eio);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(pva, count);
+
+ poperror();
+ qunlock(&inlock);
+
+ time.tv_sec = 0; /* hack around broken thread scheduler in Solaris */
+ time.tv_nsec= 1;
+ nanosleep(&time,nil);
+
+ return count;
+}
+
+long
+audio_file_write(Chan *c, void *va, long count, vlong offset)
+{
+ struct timespec time;
+ long status = -1;
+ long ba, total, chunk, bufsz;
+
+ qlock(&outlock);
+ if(waserror()){
+ qunlock(&outlock);
+ nexterror();
+ }
+
+ if(audio_file_out < 0)
+ error(Eperm);
+
+ /* check block alignment */
+ ba = av.out.bits * av.out.chan / Bits_Per_Byte;
+
+ if(count % ba)
+ error(Ebadarg);
+
+ if(audio_swap_flag && av.out.bits == 16)
+ audio_swap_endian(va, count);
+
+ total = 0;
+ bufsz = av.out.buf * Audio_Max_Buf / Audio_Max_Val;
+
+ if(bufsz == 0)
+ error(Ebadarg);
+
+ while(total < count) {
+ chunk = min(bufsz, count - total);
+ osenter();
+ status = write(audio_file_out, va, chunk);
+ osleave();
+ if(status <= 0)
+ error(Eio);
+ total += status;
+ }
+
+ poperror();
+ qunlock(&outlock);
+
+ time.tv_sec = 0; /* hack around broken thread scheduler in Solaris */
+ time.tv_nsec= 1;
+ nanosleep(&time,nil);
+
+ return count;
+}
+
+int
+audio_open_in(void)
+{
+ int fd;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_RDONLY|O_NONBLOCK);
+
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ close(fd);
+ error(Eio);
+ }
+
+ if(!audio_pause_in(fd, A_Pause)) {
+ close(fd);
+ error(Eio);
+ }
+
+ if(!audio_flush(fd, A_In)) {
+ close(fd);
+ error(Eio);
+ }
+
+ /* set audio info */
+ av.in.flags = ~0;
+ av.out.flags = 0;
+
+ if(!audio_set_info(fd, &av.in, A_In)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.in.flags = 0;
+
+ /* tada, we're open, blocking, paused and flushed */
+ return fd;
+}
+
+int
+audio_open_out(void)
+{
+ int fd;
+ struct audio_info hdr;
+
+ /* open non-blocking in case someone already has it open */
+ /* otherwise we would block until they close! */
+ fd = open(AUDIO_FILE_STRING, O_WRONLY|O_NONBLOCK);
+
+ if(fd < 0)
+ oserror();
+
+ /* change device to be blocking */
+ if(!audio_set_blocking(fd)) {
+ close(fd);
+ error("cannot set blocking mode");
+ }
+
+ /* set audio info */
+ av.in.flags = 0;
+ av.out.flags = ~0;
+
+ if(!audio_set_info(fd, &av.out, A_Out)) {
+ close(fd);
+ error(Ebadarg);
+ }
+
+ av.out.flags = 0;
+
+ return fd;
+}
+
+long
+audio_ctl_write(Chan *c, void *va, long count, vlong offset)
+{
+ int fd;
+ int ff;
+ Audio_t tmpav = av;
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ if (!audioparse(va, count, &tmpav))
+ error(Ebadarg);
+
+ if (!audio_enforce(&tmpav))
+ error(Ebadarg);
+
+ qlock(&inlock);
+ if (waserror()) {
+ qunlock(&inlock);
+ nexterror();
+ }
+
+ if (audio_file_in >= 0 && (tmpav.in.flags & AUDIO_MOD_FLAG)) {
+ if (!audio_pause_in(audio_file_in, A_Pause))
+ error(Ebadarg);
+ if (!audio_flush(audio_file_in, A_In))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_in, &tmpav.in, A_In))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&inlock);
+
+ qlock(&outlock);
+ if (waserror()) {
+ qunlock(&outlock);
+ nexterror();
+ }
+ if (audio_file_out >= 0 && (tmpav.out.flags & AUDIO_MOD_FLAG)){
+ if (!audio_pause_out(audio_file_out))
+ error(Ebadarg);
+ if (!audio_set_info(audio_file_out, &tmpav.out, A_Out))
+ error(Ebadarg);
+ }
+ poperror();
+ qunlock(&outlock);
+
+ tmpav.in.flags = 0;
+ tmpav.out.flags = 0;
+
+ av = tmpav;
+
+ return count;
+}
+
+static int
+audio_set_blocking(int fd)
+{
+ int val;
+
+ if((val = fcntl(fd, F_GETFL, 0)) == -1)
+ return 0;
+
+ val &= ~O_NONBLOCK;
+
+ if(fcntl(fd, F_SETFL, val) < 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+audio_set_info(int fd, Audio_d *i, int d)
+{
+ int status;
+ int unequal_stereo = 0;
+ audio_info_t info;
+ audio_prinfo_t *dev;
+
+ if(fd < 0)
+ return 0;
+
+ /* devitialize header */
+ AUDIO_INITINFO(&info);
+
+ if(d == A_In)
+ dev = &info.record;
+ else
+ dev = &info.play;
+
+ /* sample rate */
+ if(i->flags & AUDIO_RATE_FLAG)
+ dev->sample_rate = i->rate;
+
+ /* channels */
+ if(i->flags & AUDIO_CHAN_FLAG)
+ dev->channels = i->chan;
+
+ /* precision */
+ if(i->flags & AUDIO_BITS_FLAG)
+ dev->precision = i->bits;
+
+ /* encoding */
+ if(i->flags & AUDIO_ENC_FLAG)
+ dev->encoding = i->enc;
+
+ /* devices */
+ if(i->flags & AUDIO_DEV_FLAG)
+ dev->port = i->dev;
+
+ /* dev volume */
+ if(i->flags & (AUDIO_LEFT_FLAG|AUDIO_VOL_FLAG)) {
+ dev->gain = (i->left * AUDIO_MAX_GAIN) / Audio_Max_Val;
+
+ /* do left first then right later */
+ if(i->left == i->right)
+ dev->balance = AUDIO_MID_BALANCE;
+ else {
+ dev->balance = AUDIO_LEFT_BALANCE;
+ if(i->chan != 1)
+ unequal_stereo = 1;
+ }
+ }
+
+ osenter();
+ status = ioctl(fd, AUDIO_SETINFO, &info); /* qlock and load general stuff */
+ osleave();
+
+ if(status == -1) {
+ if(debug) print("audio_set_info 1 failed: fd = %d errno = %d\n", fd, errno);
+ return 0;
+ }
+
+ /* check for different right and left for dev */
+ if(unequal_stereo) {
+
+ /* re-init header */
+ AUDIO_INITINFO(&info);
+
+ dev->gain = (i->right * AUDIO_MAX_GAIN) / Audio_Max_Val;
+ dev->balance == AUDIO_RIGHT_BALANCE;
+
+ osenter();
+ status = ioctl(fd, AUDIO_SETINFO, &info);
+ osleave();
+
+ if(status == -1) {
+ if(debug) print("audio_set_info 2 failed: fd = %d errno = %d\n",fd, errno);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+void
+audio_swap_endian(char *p, int n)
+{
+ int b;
+
+ while (n > 1) {
+ b = p[0];
+ p[0] = p[1];
+ p[1] = b;
+ p += 2;
+ n -= 2;
+ }
+}
+
+static int
+audio_pause_out(int fd)
+{
+ audio_info_t info;
+ int foo = 0;
+ int status;
+
+ osenter();
+ status = ioctl(fd, AUDIO_DRAIN, &foo);
+ osleave();
+
+ if(status == -1)
+ return 0;
+ return 1;
+}
+
+static int
+audio_pause_in(int fd, int f)
+{
+ audio_info_t info;
+ int status;
+
+ if(fd < 0)
+ return 0;
+
+ if(audio_in_pause == f)
+ return 1;
+
+ /* initialize header */
+ AUDIO_INITINFO(&info);
+
+ /* unpause input */
+ if(f == A_Pause)
+ info.record.pause = 1;
+ else
+ info.record.pause = 0;
+
+ osenter();
+ status = ioctl(fd, AUDIO_SETINFO, &info);
+ osleave();
+
+ if(status == -1)
+ return 0;
+
+ audio_in_pause = f;
+
+ return 1;
+}
+
+static int
+audio_flush(int fd, int d)
+{
+ int flag = d==A_In? FLUSHR: FLUSHW;
+ int status;
+
+ osenter();
+ status = ioctl(fd, I_FLUSH, flag); /* drain anything already put into buffer */
+ osleave();
+
+ if(status == -1)
+ return 0;
+ return 1;
+}
+
+static int
+audio_enforce(Audio_t *t)
+{
+ if((t->in.enc == Audio_Ulaw_Val || t->in.enc == Audio_Alaw_Val) &&
+ (t->in.rate != 8000 || t->in.chan != 1))
+ return 0;
+ if((t->out.enc == Audio_Ulaw_Val || t->out.enc == Audio_Alaw_Val) &&
+ (t->out.rate != 8000 || t->out.chan != 1))
+ return 0;
+ return 1;
+}
+
+Audio_t*
+getaudiodev(void)
+{
+ return &av;
+}