#include <sys/mman.h>
#include <sys/soundcard.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct config {
char *device;
int mode;
int fd;
int format;
int sample_count;
int sample_rate;
int sample_size;
int chsamples;
int mmap;
void *buf;
oss_audioinfo audio_info;
audio_buf_info buffer_info;
};
static inline int
size2exp(int x)
{
int exp = 0;
while ((1 << exp) < x)
exp++;
return (exp);
}
static void
oss_init(struct config *config)
{
unsigned long request = SNDCTL_DSP_GETOSPACE;
int tmp = 0, prot = 0;
if ((config->fd = open(config->device, config->mode)) < 0)
err(1, "Error opening the device %s", config->device);
if (ioctl(config->fd, SNDCTL_ENGINEINFO, &config->audio_info) < 0)
err(1, "Unable to get device info");
if (ioctl(config->fd, SNDCTL_DSP_GETCAPS, &config->audio_info.caps) < 0)
err(1, "Unable to get capabilities");
if (!(config->audio_info.caps & PCM_CAP_TRIGGER))
errx(1, "Device doesn't support triggering!\n");
if (config->mmap) {
if (!(config->audio_info.caps & PCM_CAP_MMAP))
errx(1, "Device doesn't support mmap mode!\n");
tmp = 0;
if (ioctl(config->fd, SNDCTL_DSP_COOKEDMODE, &tmp) < 0)
err(1, "Unable to set cooked mode");
}
tmp = config->format;
if (ioctl(config->fd, SNDCTL_DSP_SETFMT, &tmp) < 0)
err(1, "Unable to set sample format");
if (tmp != config->format)
warnx("Format: requested=%08x, got=%08x", config->format, tmp);
config->format = tmp;
tmp = config->audio_info.max_channels;
if (ioctl(config->fd, SNDCTL_DSP_CHANNELS, &tmp) < 0)
err(1, "Unable to set channels");
if (tmp != config->audio_info.max_channels)
warnx("Channels: requested=%d, got=%d", config->audio_info.max_channels, tmp);
config->audio_info.max_channels = tmp;
tmp = config->sample_rate;
if (ioctl(config->fd, SNDCTL_DSP_SPEED, &config->sample_rate) < 0)
err(1, "Unable to set sample rate");
if (tmp != config->sample_rate)
warnx("Sample rate: requested=%d, got=%d", config->sample_rate, tmp);
config->sample_rate = tmp;
switch (config->format) {
case AFMT_S8:
case AFMT_U8:
config->sample_size = 1;
break;
case AFMT_S16_BE:
case AFMT_S16_LE:
case AFMT_U16_BE:
case AFMT_U16_LE:
config->sample_size = 2;
break;
case AFMT_S24_BE:
case AFMT_S24_LE:
case AFMT_U24_BE:
case AFMT_U24_LE:
config->sample_size = 3;
break;
case AFMT_S32_BE:
case AFMT_S32_LE:
case AFMT_U32_BE:
case AFMT_U32_LE:
case AFMT_F32_BE:
case AFMT_F32_LE:
config->sample_size = 4;
break;
default:
errx(1, "Invalid audio format %d", config->format);
break;
}
config->buffer_info.fragments = 2;
tmp = size2exp(config->sample_size * config->audio_info.max_channels);
tmp = ((config->buffer_info.fragments) << 16) | tmp;
if (ioctl(config->fd, SNDCTL_DSP_SETFRAGMENT, &tmp) < 0)
err(1, "Unable to set fragment size");
if ((config->mode & O_ACCMODE) == O_RDONLY)
request = SNDCTL_DSP_GETISPACE;
if (ioctl(config->fd, request, &config->buffer_info) < 0)
err(1, "Unable to get buffer info");
if (config->buffer_info.fragments < 1)
config->buffer_info.fragments = config->buffer_info.fragstotal;
if (config->buffer_info.bytes < 1)
config->buffer_info.bytes = config->buffer_info.fragstotal * config->buffer_info.fragsize;
if (config->buffer_info.bytes < 1) {
errx(1, "OSS buffer error: buffer size can not be %d\n",
config->buffer_info.bytes);
}
config->sample_count = config->buffer_info.bytes / config->sample_size;
config->chsamples = config->sample_count / config->audio_info.max_channels;
printf("bytes: %d, fragments: %d, fragsize: %d, fragstotal: %d, "
"samples: %d, channels: %d, sample size: %d, sample rate: %d, "
"format: %08x\n",
config->buffer_info.bytes, config->buffer_info.fragments,
config->buffer_info.fragsize, config->buffer_info.fragstotal,
config->sample_count, config->audio_info.max_channels,
config->sample_size, config->sample_rate, config->format);
switch (config->mode & O_ACCMODE) {
case O_RDONLY:
tmp = PCM_ENABLE_INPUT;
prot = PROT_READ;
break;
case O_WRONLY:
tmp = PCM_ENABLE_OUTPUT;
prot = PROT_WRITE;
break;
case O_RDWR:
tmp = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT;
prot = PROT_READ | PROT_WRITE;
break;
default:
errx(1, "Invalid mode %d", config->mode);
break;
}
if (config->mmap) {
config->buf = mmap(NULL, config->buffer_info.bytes, prot, MAP_SHARED, config->fd, 0);
if (config->buf == MAP_FAILED)
err(1, "Memory map failed");
} else {
if ((config->buf = malloc(config->buffer_info.bytes)) == NULL)
err(1, "Allocating buffer failed");
}
if (ioctl(config->fd, SNDCTL_DSP_SETTRIGGER, &tmp) < 0)
err(1, "Failed to set trigger");
}