diff options
Diffstat (limited to 'emu/OpenBSD/audio.c')
| -rw-r--r-- | emu/OpenBSD/audio.c | 547 |
1 files changed, 547 insertions, 0 deletions
diff --git a/emu/OpenBSD/audio.c b/emu/OpenBSD/audio.c new file mode 100644 index 00000000..4f2de3f6 --- /dev/null +++ b/emu/OpenBSD/audio.c @@ -0,0 +1,547 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/filio.h> +#include "audio.h" +#include <soundcard.h> + +#define Audio_Mic_Val SOUND_MIXER_MIC +#define Audio_Linein_Val SOUND_MIXER_LINE + +#define Audio_Speaker_Val SOUND_MIXER_SPEAKER +#define Audio_Headphone_Val SOUND_MIXER_PHONEOUT +#define Audio_Lineout_Val SOUND_MIXER_VOLUME + +#define Audio_Pcm_Val AFMT_S16_LE +#define Audio_Ulaw_Val AFMT_MU_LAW +#define Audio_Alaw_Val AFMT_A_LAW + +#include "audio-tbls.c" + +#define min(a,b) ((a) < (b) ? (a) : (b)) +static int debug; + +#define AUDIO_FILE_STRING "/dev/dsp" + +enum { + A_Pause, + A_UnPause +}; + +enum { + A_In, + A_Out +}; + +static QLock inlock; +static QLock outlock; + +static int audio_file = -1; /* file in/out */ +static int audio_file_in = -1; /* copy of above when opened O_READ/O_RDWR */ +static int audio_file_out = -1; /* copy of above when opened O_WRITE/O_RDWR */ + +static int audio_swap_flag = 0; /* endian swap */ + +static int audio_in_pause = A_UnPause; + +static Audio_t av; +static int mixerleftvol[32]; +static int mixerrightvol[32]; + +static int audio_enforce(Audio_t*); +static int audio_open(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) +{ + int i; + static ushort flag = 1; + + audio_swap_flag = *((uchar*)&flag) == 0; /* big-endian? */ + audio_info_init(&av); + for (i = 0; i < 32; i++) + mixerleftvol[i] = mixerrightvol[i] = 100; +} + +void +audio_ctl_init(void) +{ +} + +void +audio_file_open(Chan *c, int omode) +{ + char ebuf[ERRMAX]; + + if (debug) + print("audio_file_open(0x%.8lux, %d)\n", c, omode); + switch(omode){ + case OREAD: + qlock(&inlock); + if(waserror()){ + qunlock(&inlock); + nexterror(); + } + + if(audio_file_in >= 0) + error(Einuse); + if (audio_file < 0) + audio_file = audio_open(); + audio_file_in = audio_file; + poperror(); + qunlock(&inlock); + break; + case OWRITE: + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + nexterror(); + } + if(audio_file_out >= 0) + error(Einuse); + if (audio_file < 0) + audio_file = audio_open(); + audio_file_out = audio_file; + poperror(); + qunlock(&outlock); + break; + case ORDWR: + qlock(&inlock); + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + qunlock(&inlock); + nexterror(); + } + if(audio_file_in >= 0 || audio_file_out >= 0) + error(Einuse); + if (audio_file < 0) + audio_file = audio_open(); + audio_file_in = audio_file_out = audio_file; + poperror(); + qunlock(&outlock); + qunlock(&inlock); + break; + } + if (debug) + print("audio_file_open: success\nin %d out %d both %d\n", + audio_file_out, audio_file_in, audio_file); +} + +void +audio_ctl_open(Chan *c, int omode) +{ + USED(c); + USED(omode); +} + +void +audio_file_close(Chan *c) +{ + switch(c->mode){ + case OREAD: + qlock(&inlock); + qlock(&outlock); + if (audio_file_out < 0) { + close(audio_file); + audio_file = -1; + } + qunlock(&outlock); + audio_file_in = -1; + qunlock(&inlock); + break; + case OWRITE: + qlock(&inlock); + qlock(&outlock); + if (audio_file_in < 0) { + close(audio_file); + audio_file = -1; + } + audio_file_out = -1; + qunlock(&outlock); + qunlock(&inlock); + break; + case ORDWR: + qlock(&inlock); + qlock(&outlock); + close(audio_file); + audio_file_in = audio_file_out = audio_file = -1; + qunlock(&outlock); + qunlock(&inlock); + break; + } +} + +void +audio_ctl_close(Chan *c) +{ +} + +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); + + 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; + + if (debug > 1) + print("audio_file_write(0x%.8lux, 0x%.8lux, %ld, %uld)\n", + c, va, count, offset); + + 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); + + return count; +} + +static int +audio_open(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_RDWR|O_NONBLOCK); + if(fd < 0) + oserror(); + + /* change device to be blocking */ + if(!audio_set_blocking(fd)) { + if (debug) + print("audio_open: failed to set blocking\n"); + close(fd); + error("cannot set blocking mode"); + } + + if (debug) + print("audio_open: blocking set\n"); + + /* 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; +} + +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 +doioctl(int fd, int ctl, int *info) +{ + int status; + osenter(); + status = ioctl(fd, ctl, info); /* qlock and load general stuff */ + osleave(); + if (status < 0) + print("doioctl(0x%.8lux, 0x%.8lux) failed %d\n", ctl, *info, errno); + return status; +} + +static int +choosefmt(Audio_d *i) +{ + int newbits, newenc; + + newbits = i->bits; + newenc = i->enc; + switch (newenc) { + case Audio_Alaw_Val: + if (newbits == 8) + return AFMT_A_LAW; + break; + case Audio_Ulaw_Val: + if (newbits == 8) + return AFMT_MU_LAW; + break; + case Audio_Pcm_Val: + if (newbits == 8) + return AFMT_U8; + else if (newbits == 16) + return AFMT_S16_LE; + break; + } + return -1; +} + +static int +audio_set_info(int fd, Audio_d *i, int d) +{ + int status; + int unequal_stereo = 0; + + if(fd < 0) + return 0; + + /* fmt */ + if(i->flags & (AUDIO_BITS_FLAG || AUDIO_ENC_FLAG)) { + int oldfmt, newfmt; + oldfmt = AFMT_QUERY; + if (doioctl(fd, SNDCTL_DSP_SETFMT, &oldfmt) < 0) + return 0; + if (debug) + print("audio_set_info: current format 0x%.8lux\n", oldfmt); + newfmt = choosefmt(i); + if (debug) + print("audio_set_info: new format 0x%.8lux\n", newfmt); + if (newfmt == -1 || newfmt != oldfmt && doioctl(fd, SNDCTL_DSP_SETFMT, &newfmt) < 0) + return 0; + } + + /* channels */ + if(i->flags & AUDIO_CHAN_FLAG) { + int channels = i->chan; + if (debug) + print("audio_set_info: new channels %d\n", channels); + if (doioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0 + || channels != i->chan) + return 0; + } + + /* sample rate */ + if(i->flags & AUDIO_RATE_FLAG) { + int speed = i->rate; + if (debug) + print("audio_set_info: new speed %d\n", speed); + if (doioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0 || speed != i->rate) + return 0; + } + + /* dev volume */ + if(i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG | AUDIO_RIGHT_FLAG)) { + int val; + if (i->flags & (AUDIO_LEFT_FLAG | AUDIO_VOL_FLAG)) + mixerleftvol[i->dev] = (i->left * 100) / Audio_Max_Val; + if (i->flags & (AUDIO_RIGHT_FLAG | AUDIO_VOL_FLAG)) + mixerrightvol[i->dev] = (i->right * 100) / Audio_Max_Val; + val = mixerleftvol[i->dev] | (mixerrightvol[i->dev] << 8); + doioctl(fd, MIXER_WRITE(i->dev), &val); + } + + if (i->flags & AUDIO_DEV_FLAG) { + } + + 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) +{ + USED(fd); + return 1; +} + +static int +audio_pause_in(int fd, int f) +{ + USED(fd); + USED(f); + return 1; +} + +static int +audio_flush(int fd, int d) +{ + int x; + return doioctl(fd, SNDCTL_DSP_SYNC, &x) >= 0; +} + +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; +} |
