#define AUDIO_CAP "oss"
#include "audio_int.h"
+#if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY
+#define USE_DSP_POLICY
+#endif
+
typedef struct OSSVoiceOut {
HWVoiceOut hw;
void *pcm_buf;
int fd;
+ int wpos;
int nfrags;
int fragsize;
int mmapped;
+ int pending;
} OSSVoiceOut;
typedef struct OSSVoiceIn {
return audio_pcm_sw_write (sw, buf, len);
}
-static int aud_to_ossfmt (audfmt_e fmt)
+static int aud_to_ossfmt (audfmt_e fmt, int endianness)
{
switch (fmt) {
case AUD_FMT_S8:
return AFMT_U8;
case AUD_FMT_S16:
- return AFMT_S16_LE;
+ if (endianness) {
+ return AFMT_S16_BE;
+ }
+ else {
+ return AFMT_S16_LE;
+ }
case AUD_FMT_U16:
- return AFMT_U16_LE;
+ if (endianness) {
+ return AFMT_U16_BE;
+ }
+ else {
+ return AFMT_U16_LE;
+ }
default:
dolog ("Internal logic error: Bad audio format %d\n", fmt);
}
#endif
+#ifdef USE_DSP_POLICY
+static int oss_get_version (int fd, int *version, const char *typ)
+{
+ if (ioctl (fd, OSS_GETVERSION, &version)) {
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ /*
+ * Looks like atm (20100109) FreeBSD knows OSS_GETVERSION
+ * since 7.x, but currently only on the mixer device (or in
+ * the Linuxolator), and in the native version that part of
+ * the code is in fact never reached so the ioctl fails anyway.
+ * Until this is fixed, just check the errno and if its what
+ * FreeBSD's sound drivers return atm assume they are new enough.
+ */
+ if (errno == EINVAL) {
+ *version = 0x040000;
+ return 0;
+ }
+#endif
+ oss_logerr2 (errno, typ, "Failed to get OSS version\n");
+ return -1;
+ }
+ return 0;
+}
+#endif
+
static int oss_open (int in, struct oss_params *req,
struct oss_params *obt, int *pfd)
{
int fd;
- int version;
int oflags = conf.exclusive ? O_EXCL : 0;
audio_buf_info abinfo;
int fmt, freq, nchannels;
+ int setfragment = 1;
const char *dspname = in ? conf.devpath_in : conf.devpath_out;
const char *typ = in ? "ADC" : "DAC";
goto err;
}
- if (ioctl (fd, OSS_GETVERSION, &version)) {
- oss_logerr2 (errno, typ, "Failed to get OSS version\n");
- version = 0;
- }
+#ifdef USE_DSP_POLICY
+ if (conf.policy >= 0) {
+ int version;
- if (conf.debug) {
- dolog ("OSS version = %#x\n", version);
- }
+ if (!oss_get_version (fd, &version, typ)) {
+ if (conf.debug) {
+ dolog ("OSS version = %#x\n", version);
+ }
-#ifdef SNDCTL_DSP_POLICY
- if (conf.policy >= 0 && version >= 0x040000) {
- int policy = conf.policy;
- if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
- oss_logerr2 (errno, typ, "Failed to set timing policy to %d\n",
- conf.policy);
- goto err;
+ if (version >= 0x040000) {
+ int policy = conf.policy;
+ if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) {
+ oss_logerr2 (errno, typ,
+ "Failed to set timing policy to %d\n",
+ conf.policy);
+ goto err;
+ }
+ setfragment = 0;
+ }
}
}
- else
#endif
- {
+
+ if (setfragment) {
int mmmmssss = (req->nfrags << 16) | ctz32 (req->fragsize);
if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
return -1;
}
-static int oss_run_out (HWVoiceOut *hw)
+static void oss_write_pending (OSSVoiceOut *oss)
+{
+ HWVoiceOut *hw = &oss->hw;
+
+ if (oss->mmapped) {
+ return;
+ }
+
+ while (oss->pending) {
+ int samples_written;
+ ssize_t bytes_written;
+ int samples_till_end = hw->samples - oss->wpos;
+ int samples_to_write = audio_MIN (oss->pending, samples_till_end);
+ int bytes_to_write = samples_to_write << hw->info.shift;
+ void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift);
+
+ bytes_written = write (oss->fd, pcm, bytes_to_write);
+ if (bytes_written < 0) {
+ if (errno != EAGAIN) {
+ oss_logerr (errno, "failed to write %d bytes\n",
+ bytes_to_write);
+ }
+ break;
+ }
+
+ if (bytes_written & hw->info.align) {
+ dolog ("misaligned write asked for %d, but got %zd\n",
+ bytes_to_write, bytes_written);
+ return;
+ }
+
+ samples_written = bytes_written >> hw->info.shift;
+ oss->pending -= samples_written;
+ oss->wpos = (oss->wpos + samples_written) % hw->samples;
+ if (bytes_written - bytes_to_write) {
+ break;
+ }
+ }
+}
+
+static int oss_run_out (HWVoiceOut *hw, int live)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
- int err, rpos, live, decr;
- int samples;
- uint8_t *dst;
- struct st_sample *src;
+ int err, decr;
struct audio_buf_info abinfo;
struct count_info cntinfo;
int bufsize;
- live = audio_pcm_hw_get_live_out (hw);
- if (!live) {
- return 0;
- }
-
bufsize = hw->samples << hw->info.shift;
if (oss->mmapped) {
if (abinfo.bytes > bufsize) {
if (conf.debug) {
dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
- "please report your OS/audio hw to malc@pulsesoft.com\n",
+ "please report your OS/audio hw to av1474@comtv.ru\n",
abinfo.bytes, bufsize);
}
abinfo.bytes = bufsize;
}
}
- samples = decr;
- rpos = hw->rpos;
- while (samples) {
- int left_till_end_samples = hw->samples - rpos;
- int convert_samples = audio_MIN (samples, left_till_end_samples);
-
- src = hw->mix_buf + rpos;
- dst = advance (oss->pcm_buf, rpos << hw->info.shift);
-
- hw->clip (dst, src, convert_samples);
- if (!oss->mmapped) {
- int written;
-
- written = write (oss->fd, dst, convert_samples << hw->info.shift);
- /* XXX: follow errno recommendations ? */
- if (written == -1) {
- oss_logerr (
- errno,
- "Failed to write %d bytes of audio data from %p\n",
- convert_samples << hw->info.shift,
- dst
- );
- continue;
- }
-
- if (written != convert_samples << hw->info.shift) {
- int wsamples = written >> hw->info.shift;
- int wbytes = wsamples << hw->info.shift;
- if (wbytes != written) {
- dolog ("warning: Misaligned write %d (requested %d), "
- "alignment %d\n",
- wbytes, written, hw->info.align + 1);
- }
- decr -= wsamples;
- rpos = (rpos + wsamples) % hw->samples;
- break;
- }
- }
+ decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
+ oss->pending += decr;
+ oss_write_pending (oss);
- rpos = (rpos + convert_samples) % hw->samples;
- samples -= convert_samples;
- }
-
- hw->rpos = rpos;
return decr;
}
oss->fd = -1;
- req.fmt = aud_to_ossfmt (as->fmt);
+ req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq;
req.nchannels = as->nchannels;
req.fragsize = conf.fragsize;
static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
int trig;
- va_list ap;
- int poll_mode;
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
- va_start (ap, cmd);
- poll_mode = va_arg (ap, int);
- va_end (ap);
-
switch (cmd) {
case VOICE_ENABLE:
- ldebug ("enabling voice\n");
- if (poll_mode && oss_poll_out (hw)) {
- poll_mode = 0;
- }
- hw->poll_mode = poll_mode;
+ {
+ va_list ap;
+ int poll_mode;
- if (!oss->mmapped) {
- return 0;
- }
+ va_start (ap, cmd);
+ poll_mode = va_arg (ap, int);
+ va_end (ap);
- audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
- trig = PCM_ENABLE_OUTPUT;
- if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
- oss_logerr (
- errno,
- "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
- );
- return -1;
+ ldebug ("enabling voice\n");
+ if (poll_mode && oss_poll_out (hw)) {
+ poll_mode = 0;
+ }
+ hw->poll_mode = poll_mode;
+
+ if (!oss->mmapped) {
+ return 0;
+ }
+
+ audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
+ trig = PCM_ENABLE_OUTPUT;
+ if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+ oss_logerr (
+ errno,
+ "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
+ );
+ return -1;
+ }
}
break;
oss->fd = -1;
- req.fmt = aud_to_ossfmt (as->fmt);
+ req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.freq = as->freq;
req.nchannels = as->nchannels;
req.fragsize = conf.fragsize;
hw->info.align + 1);
}
read_samples += nread >> hwshift;
- hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
- &nominal_volume);
+ hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift);
}
if (bufs[i].len - nread) {
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
- va_list ap;
- int poll_mode;
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
- va_start (ap, cmd);
- poll_mode = va_arg (ap, int);
- va_end (ap);
-
switch (cmd) {
case VOICE_ENABLE:
- if (poll_mode && oss_poll_in (hw)) {
- poll_mode = 0;
+ {
+ va_list ap;
+ int poll_mode;
+
+ va_start (ap, cmd);
+ poll_mode = va_arg (ap, int);
+ va_end (ap);
+
+ if (poll_mode && oss_poll_in (hw)) {
+ poll_mode = 0;
+ }
+ hw->poll_mode = poll_mode;
}
- hw->poll_mode = poll_mode;
break;
case VOICE_DISABLE:
.valp = &conf.exclusive,
.descr = "Open device in exclusive mode (vmix wont work)"
},
-#ifdef SNDCTL_DSP_POLICY
+#ifdef USE_DSP_POLICY
{
.name = "POLICY",
.tag = AUD_OPT_INT,