242 lines
5.4 KiB
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);
|
|
}
|
|
|