diff options
| author | forsyth <forsyth@vitanuova.com> | 2010-03-05 11:39:25 +0000 |
|---|---|---|
| committer | forsyth <forsyth@vitanuova.com> | 2010-03-05 11:39:25 +0000 |
| commit | 79272b32d1a7e81e3c7c73b8f127b2b5f9322bed (patch) | |
| tree | af615c635b71a7785f6243f1c84ad6e6e114d193 /emu/Linux | |
| parent | ebcf8d9b30a1bc12ed7da82c9b1bae3bf11ef0dd (diff) | |
20100305-1137
Diffstat (limited to 'emu/Linux')
| -rw-r--r-- | emu/Linux/audio-oss.c | 441 | ||||
| -rw-r--r-- | emu/Linux/emu | 8 |
2 files changed, 442 insertions, 7 deletions
diff --git a/emu/Linux/audio-oss.c b/emu/Linux/audio-oss.c new file mode 100644 index 00000000..696e37c8 --- /dev/null +++ b/emu/Linux/audio-oss.c @@ -0,0 +1,441 @@ +#include "dat.h" +#include "fns.h" +#include "error.h" +#include "audio.h" +#include <sys/ioctl.h> +#include <sys/soundcard.h> + +#define Audio_Mic_Val SOUND_MIXER_MIC +#define Audio_Linein_Val SOUND_MIXER_LINE + +#define Audio_Speaker_Val SOUND_MIXER_PCM // SOUND_MIXER_VOLUME +#define Audio_Headphone_Val SOUND_MIXER_ALTPCM +#define Audio_Lineout_Val SOUND_MIXER_CD + +#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)) + +#define DEVAUDIO "/dev/dsp" +#define DEVMIXER "/dev/mixer" + +#define DPRINT if(1)print + +enum { + A_Pause, + A_UnPause, + A_In, + A_Out, +}; + +static struct { + int data; /* dsp data fd */ + int ctl; /* mixer fd */ + int pause; + QLock lk; +} afd = {.data = -1, .ctl = -1, .pause =A_UnPause }; + +static Audio_t av; +static QLock inlock; +static QLock outlock; + +static int audio_open(int); +static int audio_pause(int, int); +static int audio_set_info(int, Audio_d*, int); + +Audio_t* +getaudiodev(void) +{ + return &av; +} + +void +audio_file_init(void) +{ + audio_info_init(&av); +} + +void +audio_file_open(Chan *c, int omode) +{ + USED(c); + DPRINT("audio_file_open %d %d %x\n", afd.data, afd.ctl, omode); + qlock(&afd.lk); + if(waserror()){ + qunlock(&afd.lk); + nexterror(); + } + if(afd.data >= 0) + error(Einuse); + if(afd.ctl < 0){ + afd.ctl = open(DEVMIXER, ORDWR); + if(afd.ctl < 0) + oserror(); + } + afd.data = audio_open(omode); + if(afd.data < 0) + oserror(); + poperror(); + qunlock(&afd.lk); +} + +void +audio_file_close(Chan *c) +{ + USED(c); + DPRINT("audio_file_close %d %d\n", afd.data, afd.ctl); + qlock(&afd.lk); + if(waserror()){ + qunlock(&afd.lk); + nexterror(); + } + close(afd.data); + afd.data = -1; + qunlock(&afd.lk); + poperror(); +} + +long +audio_file_read(Chan *c, void *va, long count, vlong offset) +{ + long ba, status, chunk, total; + + USED(c); + USED(offset); + DPRINT("audio_file_read %d %d\n", afd.data, afd.ctl); + qlock(&inlock); + if(waserror()){ + qunlock(&inlock); + nexterror(); + } + + if(afd.data < 0) + error(Eperm); + + /* check block alignment */ + ba = av.in.bits * av.in.chan / Bits_Per_Byte; + + if(count % ba) + error(Ebadarg); + + if(!audio_pause(afd.data, A_UnPause)) + error(Eio); + + total = 0; + while(total < count){ + chunk = count - total; + status = read (afd.data, va + total, chunk); + if(status < 0) + error(Eio); + total += status; + } + + if(total != count) + error(Eio); + + poperror(); + qunlock(&inlock); + + return count; +} + +long +audio_file_write(Chan *c, void *va, long count, vlong offset) +{ + long status = -1; + long ba, total, chunk, bufsz; + + USED(c); + USED(offset); + DPRINT("audio_file_write %d %d\n", afd.data, afd.ctl); + qlock(&outlock); + if(waserror()){ + qunlock(&outlock); + nexterror(); + } + + if(afd.data < 0) + error(Eperm); + + /* check block alignment */ + ba = av.out.bits * av.out.chan / Bits_Per_Byte; + + if(count % ba) + error(Ebadarg); + + 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); + status = write(afd.data, va, chunk); + if(status <= 0) + error(Eio); + total += status; + } + + poperror(); + qunlock(&outlock); + + return count; +} + +long +audio_ctl_write(Chan *c, void *va, long count, vlong offset) +{ + Audio_t tmpav = av; + int tfd; + + USED(c); + USED(offset); + tmpav.in.flags = 0; + tmpav.out.flags = 0; + + DPRINT ("audio_ctl_write %X %X\n", afd.data, afd.ctl); + if(!audioparse(va, count, &tmpav)) + error(Ebadarg); + + if(!canqlock(&inlock)) + error("device busy"); + if(waserror()){ + qunlock(&inlock); + nexterror(); + } + if(!canqlock(&outlock)) + error("device busy"); + if(waserror()){ + qunlock(&outlock); + nexterror(); + } + + /* DEVAUDIO needs to be open to issue an ioctl */ + tfd = afd.data; + if(tfd < 0){ + tfd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK); + if(tfd < 0) + oserror(); + } + if(waserror()){ + if(tfd != afd.data) + close(tfd); + nexterror(); + } + + if(tmpav.in.flags & AUDIO_MOD_FLAG){ + if(!audio_pause(tfd, A_Pause)) + error(Ebadarg); + if(!audio_set_info(tfd, &tmpav.in, A_In)) + error(Ebadarg); + } + + poperror(); + if(tfd != afd.data) + close(tfd); + + tmpav.in.flags = 0; + av = tmpav; + + poperror(); + qunlock(&outlock); + poperror(); + qunlock(&inlock); + return count; +} + +/* Linux/OSS specific stuff */ + +static int +choosefmt(Audio_d *i) +{ + switch(i->bits){ + case 8: + switch(i->enc){ + case Audio_Alaw_Val: + return AFMT_A_LAW; + case Audio_Ulaw_Val: + return AFMT_MU_LAW; + case Audio_Pcm_Val: + return AFMT_U8; + } + break; + case 16: + if(i->enc == Audio_Pcm_Val) + return AFMT_S16_LE; + break; + } + return -1; +} + +static int +setvolume(int fd, int what, int left, int right) +{ + int can, v; + + if(ioctl(fd, SOUND_MIXER_READ_DEVMASK, &can) < 0) + can = ~0; + + DPRINT("setvolume fd%d %X can mix 0x%X (mask %X)\n", fd, what, (can & (1<<what)), can); + if(!(can & (1<<what))) + return 0; + v = left | (right<<8); + if(ioctl(afd.ctl, MIXER_WRITE(what), &v) < 0) + return 0; + return 1; +} + +static int +audio_set_info(int fd, Audio_d *i, int d) +{ + int status, arg; + int oldfmt, newfmt; + + USED(d); + DPRINT("audio_set_info (%d) %d %d\n", fd, afd.data, afd.ctl); + if(fd < 0) + return 0; + + /* sample rate */ + if(i->flags & AUDIO_RATE_FLAG){ + arg = i->rate; + if(ioctl(fd, SNDCTL_DSP_SPEED, &arg) < 0) + return 0; + } + + /* channels */ + if(i->flags & AUDIO_CHAN_FLAG){ + arg = i->chan; + if(ioctl(fd, SNDCTL_DSP_CHANNELS, &arg) < 0) + return 0; + } + + /* precision */ + if(i->flags & AUDIO_BITS_FLAG){ + arg = i->bits; + if(ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &arg) < 0) + return 0; + } + + /* encoding */ + if(i->flags & AUDIO_ENC_FLAG){ + ioctl(fd, SNDCTL_DSP_GETFMTS, &oldfmt); + + newfmt = choosefmt(i); + if(newfmt < 0) + return 0; + if(newfmt != oldfmt){ + status = ioctl(fd, SNDCTL_DSP_SETFMT, &arg); + DPRINT ("enc oldfmt newfmt %x status %d\n", oldfmt, newfmt, status); + } + } + + /* dev volume */ + if(i->flags & (AUDIO_LEFT_FLAG|AUDIO_VOL_FLAG)) + return setvolume(afd.ctl, i->dev, i->left, i->right); + + return 1; +} + +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_open(int omode) +{ + int fd; + + /* open non-blocking in case someone already has it open */ + /* otherwise we would block until they close! */ + switch (omode){ + case OREAD: + fd = open(DEVAUDIO, O_RDONLY|O_NONBLOCK); + break; + case OWRITE: + fd = open(DEVAUDIO, O_WRONLY|O_NONBLOCK); + break; + case ORDWR: + fd = open(DEVAUDIO, O_RDWR|O_NONBLOCK); + break; + } + + DPRINT("audio_open %d\n", fd); + if(fd < 0) + oserror(); + + /* change device to be blocking */ + if(!audio_set_blocking(fd)){ + close(fd); + error("cannot set blocking mode"); + } + + if(!audio_pause(fd, A_Pause)){ + 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; +} + +static int +dspsync(int fd) +{ + return ioctl(fd, SNDCTL_DSP_RESET, NULL) >= 0 && + ioctl(fd, SNDCTL_DSP_SYNC, NULL) >= 0; +} + +static int +audio_pause(int fd, int f) +{ + int status; + +// DPRINT ("audio_pause (%d) %d %d\n", fd, afd.data, afd.ctl); + if(fd < 0) + return 0; + if(fd != afd.data) + return dspsync(fd); + qlock(&afd.lk); + if(afd.pause == f){ + qunlock(&afd.lk); + return 1; + } + if(waserror()){ + qunlock(&afd.lk); + nexterror(); + } + status = dspsync(afd.data); + if(status) + afd.pause = f; + poperror(); + qunlock(&afd.lk); + return status; +} diff --git a/emu/Linux/emu b/emu/Linux/emu index b35aad47..be653b4b 100644 --- a/emu/Linux/emu +++ b/emu/Linux/emu @@ -20,7 +20,7 @@ dev ip ipif-posix ipaux eia -# audio audio +# audio audio-oss mem lib @@ -93,14 +93,8 @@ root /chan / /nvfs / /env / -# /chan -# /dev # /dis -# /env # /n -# /net -# /nvfs / -# /prog # /icons # /osinit.dis # /dis/emuinit.dis |
