fceux/attic/sexyal/drivers/oss.c

242 lines
5.4 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sched.h>
#include <sys/soundcard.h>
#include "../sexyal.h"
#include "../md5.h"
#include "../smallc.h"
#include "oss.h"
#define IDBASE 0x1000
void SexyALI_OSS_Enumerate(int (*func)(uint8_t *name, uint64_t id, void *udata), void *udata)
{
struct stat buf;
char fn[64];
unsigned int n;
n=0;
do
{
sal_strcpy(fn,"/dev/dsp");
sal_strcat(fn,sal_uinttos(n));
if(stat(fn,&buf)!=0) break;
} while(func(fn,n+IDBASE,udata));
}
static int FODevice(uint64_t id)
{
char fn[64];
if(id==SEXYAL_ID_DEFAULT)
{
sal_strcpy(fn,"/dev/dsp");
return(open(fn,O_WRONLY));
}
else if(id==SEXYAL_ID_UNUSED)
{
int x=-1;
int dspfd;
do
{
sal_strcpy(fn,"/dev/dsp");
if(x!=-1)
sal_strcat(fn,sal_uinttos(x));
dspfd=open(fn,O_WRONLY|O_NONBLOCK);
if(dspfd!=-1) break;
x++;
} while(errno!=ENOENT);
if(dspfd==-1) return(0);
fcntl(dspfd,F_SETFL,fcntl(dspfd,F_GETFL)&~O_NONBLOCK);
return(dspfd);
}
else
{
sal_strcpy(fn,"/dev/dsp");
sal_strcat(fn,sal_uinttos(id-IDBASE));
return(open(fn,O_WRONLY));
}
}
unsigned int Log2(unsigned int value)
{
int x=0;
value>>=1;
while(value)
{
value>>=1;
x++;
}
return(x?x:1);
}
SexyAL_device *SexyALI_OSS_Open(uint64_t id, SexyAL_format *format, SexyAL_buffering *buffering)
{
SexyAL_device *device;
int fd;
unsigned int temp;
if(!(fd=FODevice(id))) return(0);
/* Set sample format. */
/* TODO: Handle devices with byte order different from native byte order. */
/* TODO: Fix fragment size calculation to work well with lower/higher playback rates,
as reported by OSS.
*/
if(format->sampformat == SEXYAL_FMT_PCMU8)
temp=AFMT_U8;
else if(format->sampformat == SEXYAL_FMT_PCMS8)
temp=AFMT_S8;
else if(format->sampformat == SEXYAL_FMT_PCMU16)
temp=AFMT_U16_LE;
else
temp=AFMT_S16_NE;
format->byteorder=0;
ioctl(fd,SNDCTL_DSP_SETFMT,&temp);
switch(temp)
{
case AFMT_U8: format->sampformat = SEXYAL_FMT_PCMU8;break;
case AFMT_S8: format->sampformat = SEXYAL_FMT_PCMS8;break;
case AFMT_U16_LE:
#ifndef LSB_FIRST
format->byteorder=1;
#endif
format->sampformat = SEXYAL_FMT_PCMU16;break;
case AFMT_U16_BE:
#ifdef LSB_FIRST
format->byteorder=1;
#endif
format->sampformat = SEXYAL_FMT_PCMU16;break;
case AFMT_S16_LE:
#ifndef LSB_FIRST
format->byteorder=1;
#endif
format->sampformat = SEXYAL_FMT_PCMS16;break;
case AFMT_S16_BE:
#ifdef LSB_FIRST
format->byteorder=1;
#endif
format->sampformat = SEXYAL_FMT_PCMS16;break;
default: close(fd); return(0);
}
/* Set number of channels. */
temp=format->channels;
if(ioctl(fd,SNDCTL_DSP_CHANNELS,&temp)==-1)
{
close(fd);
return(0);
}
if(temp<1 || temp>2)
{
close(fd);
return(0);
}
format->channels=temp;
/* Set frame rate. */
temp=format->rate;
if(ioctl(fd,SNDCTL_DSP_SPEED,&temp)==-1)
{
close(fd);
return(0);
}
format->rate=temp;
device=malloc(sizeof(SexyAL_device));
sal_memcpy(&device->format,format,sizeof(SexyAL_format));
sal_memcpy(&device->buffering,buffering,sizeof(SexyAL_buffering));
if(buffering->fragcount == 0 || buffering->fragsize == 0)
{
buffering->fragcount=16;
buffering->fragsize=64;
}
else
{
if(buffering->fragsize<32) buffering->fragsize=32;
if(buffering->fragcount<2) buffering->fragcount=2;
}
if(buffering->ms)
{
int64_t tc;
//printf("%d\n",buffering->ms);
/* 2*, >>1, |1 for crude rounding(it will always round 0.5 up, so it is a bit biased). */
tc=2*buffering->ms * format->rate / 1000 / buffering->fragsize;
//printf("%f\n",(double)buffering->ms * format->rate / 1000 / buffering->fragsize);
buffering->fragcount=(tc>>1)+(tc&1); //1<<Log2(tc);
//printf("%d\n",buffering->fragcount);
}
temp=Log2(buffering->fragsize*(format->sampformat>>4)*format->channels);
temp|=buffering->fragcount<<16;
ioctl(fd,SNDCTL_DSP_SETFRAGMENT,&temp);
{
audio_buf_info info;
ioctl(fd,SNDCTL_DSP_GETOSPACE,&info);
buffering->fragsize=info.fragsize/(format->sampformat>>4)/format->channels;
buffering->fragcount=info.fragments;
buffering->totalsize=buffering->fragsize*buffering->fragcount;
//printf("Actual: %d, %d\n",buffering->fragsize,buffering->fragcount);
}
device->private=malloc(sizeof(int));
*(int*)device->private=fd;
return(device);
}
int SexyALI_OSS_Close(SexyAL_device *device)
{
if(device)
{
if(device->private)
{
close(*(int*)device->private);
free(device->private);
}
free(device);
return(1);
}
return(0);
}
uint32_t SexyALI_OSS_RawWrite(SexyAL_device *device, void *data, uint32_t len)
{
ssize_t bytes;
bytes = write(*(int *)device->private,data,len);
if(bytes <= 0) return(0); /* FIXME: What to do on -1? */
return(bytes);
}
uint32_t SexyALI_OSS_RawCanWrite(SexyAL_device *device)
{
struct audio_buf_info ai;
if(!ioctl(*(int *)device->private,SNDCTL_DSP_GETOSPACE,&ai))
return(ai.bytes);
else
return(0);
}