You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
4.5 KiB
217 lines
4.5 KiB
/* Extended Module Player |
|
* Copyright (C) 1996-2012 Claudio Matsuoka and Hipolito Carraro Jr |
|
* |
|
* This file is part of the Extended Module Player and is distributed |
|
* under the terms of the GNU General Public License. See doc/COPYING |
|
* for more information. |
|
*/ |
|
|
|
/* |
|
* devfs /dev/sound/dsp support by Dirk Jagdmann |
|
* resume/onpause by Test Rat <ttsestt@gmail.com> |
|
*/ |
|
|
|
#ifdef HAVE_CONFIG_H |
|
#include "config.h" |
|
#endif |
|
|
|
#include <string.h> |
|
#include <stdlib.h> |
|
#include <sys/time.h> |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include <fcntl.h> |
|
#include <unistd.h> |
|
#include <sys/ioctl.h> |
|
|
|
#if defined(HAVE_SYS_SOUNDCARD_H) |
|
#include <sys/soundcard.h> |
|
#elif defined(HAVE_MACHINE_SOUNDCARD_H) |
|
#include <machine/soundcard.h> |
|
#endif |
|
|
|
#include "driver.h" |
|
|
|
static int audio_fd; |
|
|
|
static void from_fmt(struct xmp_options *, int); |
|
static int to_fmt(struct xmp_options *); |
|
static void setaudio(struct xmp_options *); |
|
static int init(struct context_data *); |
|
static void shutdown(struct context_data *); |
|
static void bufdump(struct context_data *, void *, int); |
|
static void resume(void); |
|
static void onpause(void); |
|
|
|
static char *help[] = { |
|
"frag=num,size", "Set the number and size of fragments", |
|
"dev=<device_name>", "Audio device to use (default /dev/dsp)", |
|
"nosync", "Don't flush OSS buffers between modules", |
|
NULL |
|
}; |
|
|
|
struct xmp_drv_info drv_oss = { |
|
"oss", /* driver ID */ |
|
"OSS PCM audio", /* driver description */ |
|
help, /* help */ |
|
init, /* init */ |
|
shutdown, /* shutdown */ |
|
resume, /* starttimer */ |
|
onpause, /* stoptimer */ |
|
bufdump, /* bufdump */ |
|
}; |
|
|
|
static int fragnum, fragsize; |
|
static int do_sync = 1; |
|
|
|
static int to_fmt(struct xmp_options *o) |
|
{ |
|
int fmt; |
|
|
|
if (!o->resol) |
|
return AFMT_MU_LAW; |
|
|
|
if (o->resol == 8) |
|
fmt = AFMT_U8 | AFMT_S8; |
|
else { |
|
fmt = o->big_endian ? AFMT_S16_BE | AFMT_U16_BE : |
|
AFMT_S16_LE | AFMT_U16_LE; |
|
} |
|
|
|
if (o->outfmt & XMP_FMT_UNS) |
|
fmt &= AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE; |
|
else |
|
fmt &= AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE; |
|
|
|
return fmt; |
|
} |
|
|
|
static void from_fmt(struct xmp_options *o, int outfmt) |
|
{ |
|
if (outfmt & AFMT_MU_LAW) { |
|
o->resol = 0; |
|
return; |
|
} |
|
|
|
if (outfmt & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) |
|
o->resol = 16; |
|
else |
|
o->resol = 8; |
|
|
|
if (outfmt & (AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE)) |
|
o->outfmt |= XMP_FMT_UNS; |
|
else |
|
o->outfmt &= ~XMP_FMT_UNS; |
|
} |
|
|
|
static void setaudio(struct xmp_options *o) |
|
{ |
|
static int fragset = 0; |
|
int frag = 0; |
|
int fmt; |
|
|
|
frag = (fragnum << 16) + fragsize; |
|
|
|
fmt = to_fmt(o); |
|
ioctl(audio_fd, SNDCTL_DSP_SETFMT, &fmt); |
|
from_fmt(o, fmt); |
|
|
|
fmt = !(o->outfmt & XMP_FORMAT_MONO); |
|
ioctl(audio_fd, SNDCTL_DSP_STEREO, &fmt); |
|
if (fmt) |
|
o->outfmt &= ~XMP_FORMAT_MONO; |
|
else |
|
o->outfmt |= XMP_FORMAT_MONO; |
|
|
|
ioctl(audio_fd, SNDCTL_DSP_SPEED, &o->freq); |
|
|
|
/* Set the fragments only once */ |
|
if (!fragset) { |
|
if (fragnum && fragsize) |
|
ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag); |
|
fragset++; |
|
} |
|
} |
|
|
|
static int init(struct context_data *ctx) |
|
{ |
|
char *dev_audio[] = { "/dev/dsp", "/dev/sound/dsp" }; |
|
struct xmp_options *o = &ctx->o; |
|
audio_buf_info info; |
|
static char buf[80]; |
|
char *token, **parm; |
|
int i; |
|
|
|
fragnum = 16; /* default number of fragments */ |
|
i = 1024; /* default size of fragment */ |
|
|
|
parm_init(); |
|
chkparm2("frag", "%d,%d", &fragnum, &i); |
|
chkparm1("dev", dev_audio[0] = token); |
|
chkparm0("nosync", do_sync = 0); |
|
parm_end(); |
|
|
|
for (fragsize = 0; i >>= 1; fragsize++) ; |
|
if (fragsize < 4) |
|
fragsize = 4; |
|
|
|
for (i = 0; i < sizeof(dev_audio) / sizeof(dev_audio[0]); i++) |
|
if ((audio_fd = open(dev_audio[i], O_WRONLY)) >= 0) |
|
break; |
|
if (audio_fd < 0) |
|
return XMP_ERR_DINIT; |
|
|
|
setaudio(o); |
|
|
|
if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) == 0) { |
|
snprintf(buf, 80, "%s [%d fragments of %d bytes]", |
|
drv_oss.description, info.fragstotal, |
|
info.fragsize); |
|
drv_oss.description = buf; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
/* Build and write one tick (one PAL frame or 1/50 s in standard vblank |
|
* timed mods) of audio data to the output device. |
|
*/ |
|
static void bufdump(struct context_data *ctx, void *b, int i) |
|
{ |
|
int j; |
|
|
|
while (i) { |
|
if ((j = write(audio_fd, b, i)) > 0) { |
|
i -= j; |
|
b = (char *)b + j; |
|
} else |
|
break; |
|
}; |
|
} |
|
|
|
static void shutdown(struct context_data *ctx) |
|
{ |
|
ioctl(audio_fd, SNDCTL_DSP_RESET, NULL); |
|
close(audio_fd); |
|
} |
|
|
|
static void resume() |
|
{ |
|
#ifdef SNDCTL_DSP_SETTRIGGER |
|
int trig = PCM_ENABLE_OUTPUT; |
|
ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &trig); |
|
#endif |
|
} |
|
|
|
static void onpause() |
|
{ |
|
#ifdef SNDCTL_DSP_SETTRIGGER |
|
int trig = 0; |
|
ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &trig); |
|
#else |
|
ioctl(audio_fd, SNDCTL_DSP_RESET, NULL); |
|
#endif |
|
|
|
if (do_sync) |
|
ioctl(audio_fd, SNDCTL_DSP_SYNC, NULL); |
|
}
|
|
|