ref: 06873db08ba79afeb31d5d490d24fb2f043d5d15
parent: 6ada3918c43f59d76d2e0a5e29c2564c15778a0a
author: robs <robs>
date: Sun Jan 4 10:17:19 EST 2009
rewrite alsa; support 24-bit
--- a/ChangeLog
+++ b/ChangeLog
@@ -41,6 +41,8 @@
File formats:
+ o 24-bit support for ALSA driver. (robs)
+ o Warn if ALSA under/overrun. (robs)
o Slight improvement to A-law/u-law conversion accuracy: round LSB
instead of truncating. (robs)
o New `--ignore-length' option to ignore length in input file
--- a/src/alsa.c
+++ b/src/alsa.c
@@ -1,678 +1,276 @@
-/* ALSA sound handler
+/* libSoX device driver: ALSA (c) 2006-9 SoX contributors
*
- * Copyright 1997-2005 Jimen Ching And Sundry Contributors
- * This source code is freely redistributable and may be used for
- * any purpose. This copyright notice must be maintained.
- * Jimen Ching And Sundry Contributors are not
- * responsible for the consequences of using this software.
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "sox_i.h"
-
#include <alsa/asoundlib.h>
typedef struct {
- snd_pcm_t *pcm_handle;
- char *buf;
- size_t buf_size;
- snd_pcm_uframes_t period_size;
- snd_pcm_uframes_t frames_this_period;
+ snd_pcm_uframes_t buf_len, period;
+ snd_pcm_t * pcm;
+ char * buf;
+ int format;
} priv_t;
-static int get_format(sox_format_t * ft, snd_pcm_format_mask_t *fmask, int *fmt)
-{
- if (ft->encoding.bits_per_sample != 16)
- {
- lsx_report("trying for word samples.");
- ft->encoding.bits_per_sample = 16;
- }
+static const int bytes_size[] = {1, 2, 4}, encs[][3] = {
+ {SND_PCM_FORMAT_S8, SND_PCM_FORMAT_S16, SND_PCM_FORMAT_S24},
+ {SND_PCM_FORMAT_U8, SND_PCM_FORMAT_U16, SND_PCM_FORMAT_U24}};
+#define NBYTES bytes_size[(ft->encoding.bits_per_sample >> 3) - 1]
- if (ft->encoding.encoding != SOX_ENCODING_SIGN2 &&
- ft->encoding.encoding != SOX_ENCODING_UNSIGNED)
- {
- if (ft->encoding.bits_per_sample == 16)
- {
- if (ft->encoding.encoding != SOX_ENCODING_UNKNOWN)
- lsx_report("driver supports only signed and unsigned samples. Changing to signed.");
- ft->encoding.encoding = SOX_ENCODING_SIGN2;
- }
- else
- {
- if (ft->encoding.encoding != SOX_ENCODING_UNKNOWN)
- lsx_report("driver supports only signed and unsigned samples. Changing to unsigned.");
- ft->encoding.encoding = SOX_ENCODING_UNSIGNED;
- }
- }
-
- /* Some hardware only wants to work with 8-bit or 16-bit data */
- if (ft->encoding.bits_per_sample == 8)
- {
- if (!(snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_U8)) &&
- !(snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_S8)))
- {
- lsx_report("driver doesn't supported byte samples. Changing to words.");
- ft->encoding.bits_per_sample = 16;
- }
- }
- else if (ft->encoding.bits_per_sample == 16)
- {
- if (!(snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_U16)) &&
- !(snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_S16)))
- {
- lsx_report("driver doesn't supported word samples. Changing to bytes.");
- ft->encoding.bits_per_sample = 8;
- }
- }
- else
- {
- if ((snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_U16)) ||
- (snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_S16)))
- {
- lsx_report("driver doesn't supported %u-bit samples. Changing to 16-bit.", ft->encoding.bits_per_sample);
- ft->encoding.bits_per_sample = 16;
- }
- else
- {
- lsx_report("driver doesn't supported %u-bit samples. Changing to 8-bit.", ft->encoding.bits_per_sample);
- ft->encoding.bits_per_sample = 8;
- }
- }
-
- if (ft->encoding.bits_per_sample == 8) {
- switch (ft->encoding.encoding)
- {
- case SOX_ENCODING_SIGN2:
- if (!(snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_S8)))
- {
- lsx_report("driver doesn't supported signed byte samples. Changing to unsigned bytes.");
- ft->encoding.encoding = SOX_ENCODING_UNSIGNED;
- }
- break;
- case SOX_ENCODING_UNSIGNED:
- if (!(snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_U8)))
- {
- lsx_report("driver doesn't supported unsigned byte samples. Changing to signed bytes.");
- ft->encoding.encoding = SOX_ENCODING_SIGN2;
- }
- break;
- default:
- break;
- }
- switch (ft->encoding.encoding)
- {
- case SOX_ENCODING_SIGN2:
- if (!(snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_S8)))
- {
- lsx_fail_errno(ft,SOX_EFMT,"ALSA driver does not support signed byte samples");
- return SOX_EOF;
- }
- *fmt = SND_PCM_FORMAT_S8;
- break;
- case SOX_ENCODING_UNSIGNED:
- if (!(snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_U8)))
- {
- lsx_fail_errno(ft,SOX_EFMT,"ALSA driver does not support unsigned byte samples");
- return SOX_EOF;
- }
- *fmt = SND_PCM_FORMAT_U8;
- break;
- default:
- break;
- }
- }
- else if (ft->encoding.bits_per_sample == 16) {
- switch (ft->encoding.encoding)
- {
- case SOX_ENCODING_SIGN2:
- if (!(snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_S16)))
- {
- lsx_report("driver does not support signed word samples. Changing to unsigned words.");
- ft->encoding.encoding = SOX_ENCODING_UNSIGNED;
- }
- break;
- case SOX_ENCODING_UNSIGNED:
- if (!(snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_U16)))
- {
- lsx_report("driver does not support unsigned word samples. Changing to signed words.");
- ft->encoding.encoding = SOX_ENCODING_SIGN2;
- }
- break;
- default:
- break;
- }
- switch (ft->encoding.encoding)
- {
- case SOX_ENCODING_SIGN2:
- if (!(snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_S16)))
- {
- lsx_fail_errno(ft,SOX_EFMT,"ALSA driver does not support signed word samples");
- return SOX_EOF;
- }
- *fmt = SND_PCM_FORMAT_S16;
- break;
- case SOX_ENCODING_UNSIGNED:
- if (!(snd_pcm_format_mask_test(fmask, SND_PCM_FORMAT_U16)))
- {
- lsx_fail_errno(ft,SOX_EFMT,"ALSA driver does not support unsigned word samples");
- return SOX_EOF;
- }
- *fmt = SND_PCM_FORMAT_U16;
- break;
- default:
- break;
- }
- }
- else {
- lsx_fail_errno(ft,SOX_EFMT,"ALSA driver does not support %s %u-bit output",
- sox_encodings_info[(unsigned char)ft->encoding.encoding].desc, ft->encoding.bits_per_sample);
- return SOX_EOF;
- }
- return 0;
-}
+static int select_format(sox_encoding_t * e, unsigned * bits, snd_pcm_format_mask_t const * mask, int * format)
+{
+ unsigned does[3], i, j, k = (*bits >> 3) - 1;
+
+ if (k > 2 || (*e != SOX_ENCODING_SIGN2 && *e != SOX_ENCODING_UNSIGNED))
+ return -1;
+ for (i = 0; i < 3; ++i) for (does[i] = 0, j = 0; j < 2; ++j)
+ does[i] |= snd_pcm_format_mask_test(mask, encs[j][i]);
+ if (!does[k])
+ k = 1;
+ while (!does[k]) if ((k = (k + 1 ) % 3) == 1)
+ return -1;
+ *bits = (k + 1) << 3;
+
+ if (*e == SOX_ENCODING_SIGN2 && !snd_pcm_format_mask_test(mask, encs[0][k]))
+ *e = SOX_ENCODING_UNSIGNED;
+ *format = encs[*e == SOX_ENCODING_UNSIGNED][k];
+ return 0;
+}
-static int setup(sox_format_t * ft, snd_pcm_stream_t mode)
+#define _(x,y) do {if ((err = x y) < 0) {lsx_fail_errno(ft, SOX_EPERM, #x " error: %s", snd_strerror(err)); goto error;} } while (0)
+static int setup(sox_format_t * ft)
{
- int fmt = SND_PCM_FORMAT_S16;
- int err;
- priv_t * alsa = (priv_t *)ft->priv;
- snd_pcm_hw_params_t *hw_params = NULL;
- unsigned int min_rate, max_rate;
- unsigned int min_chan, max_chan;
- unsigned int rate;
- snd_pcm_uframes_t buffer_size, buffer_size_min, buffer_size_max;
- snd_pcm_uframes_t period_size, period_size_min, period_size_max;
- int dir;
- snd_pcm_format_mask_t *fmask = NULL;
- sox_signalinfo_t client_signal = ft->signal;
+ priv_t * p = (priv_t *)ft->priv;
+ snd_pcm_hw_params_t * params = NULL;
+ snd_pcm_format_mask_t * mask = NULL;
+ snd_pcm_uframes_t min, max;
+ unsigned n;
+ int err;
- lsx_set_signal_defaults(&ft->signal);
-
- if ((err = snd_pcm_open(&(alsa->pcm_handle), ft->filename,
- mode, 0)) < 0)
- {
- lsx_fail_errno(ft, SOX_EPERM, "cannot open audio device");
- goto open_error;
- }
-
- if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
- {
- lsx_fail_errno(ft, SOX_ENOMEM,
- "cannot allocate hardware parameter structure");
- goto open_error;
- }
-
- if ((err = snd_pcm_hw_params_any(alsa->pcm_handle, hw_params)) < 0)
- {
- lsx_fail_errno(ft, SOX_EPERM,
- "cannot initialize hardware parameter structure");
- goto open_error;
- }
-
-#if SND_LIB_VERSION >= 0x010009
- /* Turn off software resampling */
- err = snd_pcm_hw_params_set_rate_resample(alsa->pcm_handle, hw_params, 0);
- if (err < 0) {
- lsx_fail_errno(ft, SOX_EPERM, "Resampling setup failed for playback");
- goto open_error;
- }
+ _(snd_pcm_open, (&p->pcm, ft->filename, ft->mode == 'r'? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, 0));
+ _(snd_pcm_hw_params_malloc, (¶ms));
+ _(snd_pcm_hw_params_any, (p->pcm, params));
+#if SND_LIB_VERSION >= 0x010009 /* Disable alsa-lib resampling: */
+ _(snd_pcm_hw_params_set_rate_resample, (p->pcm, params, 0));
#endif
+ _(snd_pcm_hw_params_set_access, (p->pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED));
- if ((err = snd_pcm_hw_params_set_access(alsa->pcm_handle, hw_params,
- SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
- {
- lsx_fail_errno(ft, SOX_EPERM,
- "cannot set access type");
- goto open_error;
- }
+ _(snd_pcm_format_mask_malloc, (&mask)); /* Set format: */
+ snd_pcm_hw_params_get_format_mask(params, mask);
+ _(select_format, (&ft->encoding.encoding, &ft->encoding.bits_per_sample, mask, &p->format));
+ _(snd_pcm_hw_params_set_format, (p->pcm, params, p->format));
+ snd_pcm_format_mask_free(mask), mask = NULL;
- snd_pcm_hw_params_get_channels_min(hw_params, &min_chan);
- snd_pcm_hw_params_get_channels_max(hw_params, &max_chan);
- if (ft->signal.channels == 0)
- ft->signal.channels = min_chan;
- else
- if (ft->signal.channels > max_chan)
- ft->signal.channels = max_chan;
- else if (ft->signal.channels < min_chan)
- ft->signal.channels = min_chan;
+ n = ft->signal.rate; /* Set rate: */
+ _(snd_pcm_hw_params_set_rate_near, (p->pcm, params, &n, 0));
+ ft->signal.rate = n;
- if (snd_pcm_format_mask_malloc(&fmask) < 0)
- goto open_error;
- snd_pcm_hw_params_get_format_mask(hw_params, fmask);
+ n = ft->signal.channels; /* Set channels: */
+ _(snd_pcm_hw_params_set_channels_near, (p->pcm, params, &n));
+ ft->signal.channels = n;
- if (get_format(ft, fmask, &fmt) < 0)
- goto open_error;
+ /* Set buf_len > > sox_globals.bufsiz for no underrun: */
+ p->buf_len = sox_globals.bufsiz * 8 / NBYTES / ft->signal.channels;
+ _(snd_pcm_hw_params_get_buffer_size_min, (params, &min));
+ _(snd_pcm_hw_params_get_buffer_size_max, (params, &max));
+ p->period = range_limit(p->buf_len, min, max) / 8;
+ p->buf_len = p->period * 8;
+ _(snd_pcm_hw_params_set_period_size_near, (p->pcm, params, &p->period, 0));
+ _(snd_pcm_hw_params_set_buffer_size_near, (p->pcm, params, &p->buf_len));
+ if (p->period * 2 > p->buf_len) {
+ lsx_fail_errno(ft, SOX_EPERM, "buffer too small");
+ goto error;
+ }
- snd_pcm_format_mask_free(fmask);
- fmask = NULL;
+ _(snd_pcm_hw_params, (p->pcm, params)); /* Configure ALSA */
+ snd_pcm_hw_params_free(params), params = NULL;
+ _(snd_pcm_prepare, (p->pcm));
+ p->buf_len *= ft->signal.channels; /* No longer in `frames' */
+ p->buf = lsx_malloc(p->buf_len * NBYTES);
+ return SOX_SUCCESS;
- if ((err = snd_pcm_hw_params_set_format(alsa->pcm_handle,
- hw_params, fmt)) < 0)
- {
- lsx_fail_errno(ft, SOX_EPERM, "cannot set sample format");
- goto open_error;
- }
-
- snd_pcm_hw_params_get_rate_min(hw_params, &min_rate, &dir);
- snd_pcm_hw_params_get_rate_max(hw_params, &max_rate, &dir);
-
- rate = range_limit(ft->signal.rate, min_rate, max_rate);
- if (rate != ft->signal.rate)
- {
- if (client_signal.rate != 0)
- lsx_report("hardware does not support sample rate %g; changing to %i.", ft->signal.rate, rate);
- ft->signal.rate = rate;
- }
- dir = 0;
- if ((err = snd_pcm_hw_params_set_rate_near(alsa->pcm_handle,
- hw_params,
- &rate,
- &dir)) < 0)
- {
- lsx_fail_errno(ft, SOX_EPERM, "cannot set sample rate");
- goto open_error;
- }
- snd_pcm_hw_params_get_rate(hw_params,
- &rate,
- &dir);
-
- if (rate != ft->signal.rate)
- {
- lsx_report("Could not set exact rate of %g. Approximating with %i",
- ft->signal.rate, rate);
- }
-
- snd_pcm_hw_params_get_rate(hw_params, &rate, &dir);
-
- if ((err = snd_pcm_hw_params_set_channels(alsa->pcm_handle,
- hw_params,
- ft->signal.channels)) < 0)
- {
- lsx_fail_errno(ft, SOX_EPERM, "cannot set channel count");
- goto open_error;
- }
-
- /* Have a much larger buffer than sox_globals.bufsiz to avoid underruns */
- buffer_size = sox_globals.bufsiz * 8 / (ft->encoding.bits_per_sample >> 3) / ft->signal.channels;
-
- if (snd_pcm_hw_params_get_buffer_size_min(hw_params, &buffer_size_min) < 0)
- {
- lsx_fail_errno(ft, SOX_EPERM, "Error getting min buffer size.");
- goto open_error;
- }
-
- if (snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size_max) < 0)
- {
- lsx_fail_errno(ft, SOX_EPERM, "Error getting max buffer size.");
- goto open_error;
- }
-
- dir = 0;
- if (snd_pcm_hw_params_get_period_size_min(hw_params,
- &period_size_min, &dir) < 0)
- {
- lsx_fail_errno(ft, SOX_EPERM, "Error getting min period size.");
- goto open_error;
- }
-
- dir = 0;
- if (snd_pcm_hw_params_get_period_size_max(hw_params,
- &period_size_max, &dir) < 0)
- {
- lsx_fail_errno(ft, SOX_EPERM, "Error getting max buffer size.");
- goto open_error;
- }
-
- if (buffer_size_max < buffer_size)
- buffer_size = buffer_size_max;
- else if (buffer_size_min > buffer_size)
- buffer_size = buffer_size_min;
-
- period_size = buffer_size / 8;
- buffer_size = period_size * 8;
-
- dir = 0;
- if (snd_pcm_hw_params_set_period_size_near(alsa->pcm_handle, hw_params,
- &period_size, &dir) < 0)
- {
- lsx_fail_errno(ft, SOX_EPERM, "Error setting periods.");
- goto open_error;
- }
- snd_pcm_hw_params_get_period_size(hw_params, &period_size, &dir);
-
- dir = 0;
- if (snd_pcm_hw_params_set_buffer_size_near(alsa->pcm_handle, hw_params,
- &buffer_size) < 0) {
- lsx_fail_errno(ft, SOX_EPERM, "Error setting buffer size.");
- goto open_error;
- }
-
- snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_size);
- if (period_size * 2 > buffer_size)
- {
- lsx_fail_errno(ft, SOX_EPERM, "Buffer too small. Could not use.");
- goto open_error;
- }
-
- if ((err = snd_pcm_hw_params(alsa->pcm_handle, hw_params)) < 0)
- {
- lsx_fail_errno(ft, SOX_EPERM, "cannot set parameters");
- goto open_error;
- }
-
- snd_pcm_hw_params_free(hw_params);
- hw_params = NULL;
-
- if ((err = snd_pcm_prepare(alsa->pcm_handle)) < 0)
- {
- lsx_fail_errno(ft, SOX_EPERM, "cannot prepare audio interface for use");
- goto open_error;
- }
-
- alsa->buf_size = buffer_size * (ft->encoding.bits_per_sample >> 3) * ft->signal.channels;
- alsa->period_size = period_size;
- alsa->frames_this_period = 0;
- alsa->buf = lsx_malloc(alsa->buf_size);
-
- return (SOX_SUCCESS);
-
-open_error:
- if (fmask)
- snd_pcm_format_mask_free(fmask);
- if (hw_params)
- snd_pcm_hw_params_free(hw_params);
-
- return SOX_EOF;
-
+error:
+ if (mask) snd_pcm_format_mask_free(mask);
+ if (params) snd_pcm_hw_params_free(params);
+ return SOX_EOF;
}
-/*
- * Over/underrun and suspend recovery
- */
-static int xrun_recovery(snd_pcm_t *handle, int err)
+static int recover(sox_format_t * ft, snd_pcm_t * pcm, int err)
{
- if (err == -EPIPE)
- { /* over/under-run */
- err = snd_pcm_prepare(handle);
- if (err < 0)
- lsx_warn("Can't recover from over/underrun, prepare failed: %s", snd_strerror(err));
- return 0;
- }
- else
- {
- if (err == -ESTRPIPE)
- {
- /* wait until the suspend flag is released */
- while ((err = snd_pcm_resume(handle)) == -EAGAIN)
- sleep(1);
- if (err < 0)
- {
- err = snd_pcm_prepare(handle);
- if (err < 0)
- lsx_warn("Can't recovery from suspend, prepare failed: %s", snd_strerror(err));
- }
- return 0;
- }
- }
- return err;
+ if (err == -EPIPE)
+ lsx_warn("%s-run", ft->mode == 'r'? "over" : "under");
+ else if (err != -ESTRPIPE)
+ lsx_warn("%s", snd_strerror(err));
+ else while ((err = snd_pcm_resume(pcm)) == -EAGAIN) {
+ lsx_report("suspended");
+ sleep(1); /* Wait until the suspend flag is released */
+ }
+ if (err < 0 && (err = snd_pcm_prepare(pcm)) < 0)
+ lsx_fail_errno(ft, SOX_EPERM, "%s", snd_strerror(err));
+ return err;
}
-/*
- * Do anything required before you start reading samples.
- * Read file header.
- * Find out sampling rate,
- * size and encoding of samples,
- * mono/stereo/quad.
- */
-static int startread(sox_format_t * ft)
+static size_t read_(sox_format_t * ft, sox_sample_t * buf, size_t len)
{
- return setup(ft, SND_PCM_STREAM_CAPTURE);
-}
+ priv_t * p = (priv_t *)ft->priv;
+ snd_pcm_sframes_t i, n;
+ size_t done;
-static void ub_read_buf(sox_sample_t *buf1, char const * buf2, size_t len, sox_bool swap UNUSED, size_t * clips UNUSED)
-{
- while (len--)
- *buf1++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(*((unsigned char *)buf2++),);
-}
+ len = min(len, p->buf_len);
+ for (done = 0; done < len; done += n) {
+ do {
+ n = snd_pcm_readi(p->pcm, p->buf, (len - done) / ft->signal.channels);
+ if (n < 0 && recover(ft, p->pcm, n) < 0)
+ return 0;
+ } while (n <= 0);
-static void sb_read_buf(sox_sample_t *buf1, char const * buf2, size_t len, sox_bool swap UNUSED, size_t * clips UNUSED)
-{
- while (len--)
- *buf1++ = SOX_SIGNED_8BIT_TO_SAMPLE(*((int8_t *)buf2++),);
-}
-
-static void uw_read_buf(sox_sample_t *buf1, char const * buf2, size_t len, sox_bool swap, size_t * clips UNUSED)
-{
- while (len--)
- {
- uint16_t datum = *((uint16_t *)buf2);
- buf2++; buf2++;
- if (swap)
- datum = lsx_swapw(datum);
-
- *buf1++ = SOX_UNSIGNED_16BIT_TO_SAMPLE(datum,);
- }
-}
-
-static void sw_read_buf(sox_sample_t *buf1, char const * buf2, size_t len, sox_bool swap, size_t * clips UNUSED)
-{
- while (len--)
- {
- int16_t datum = *((int16_t *)buf2);
- buf2++; buf2++;
- if (swap)
- datum = lsx_swapw(datum);
-
- *buf1++ = SOX_SIGNED_16BIT_TO_SAMPLE(datum,);
- }
-}
-
-static size_t read_samples(sox_format_t * ft, sox_sample_t *buf, size_t nsamp)
-{
- priv_t * alsa = (priv_t *)ft->priv;
- void (*read_buf)(sox_sample_t *, char const *, size_t, sox_bool, size_t *) = 0;
- size_t len;
-
- switch(ft->encoding.bits_per_sample) {
- case 8:
- switch(ft->encoding.encoding) {
- case SOX_ENCODING_SIGN2: read_buf = sb_read_buf; break;
- case SOX_ENCODING_UNSIGNED: read_buf = ub_read_buf; break;
- default:
- lsx_fail_errno(ft,SOX_EFMT,"Do not support this encoding for this data size");
- return 0;
- }
+ i = n *= ft->signal.channels;
+ switch (p->format) {
+ case SND_PCM_FORMAT_S8: {
+ int8_t * buf1 = (int8_t *)p->buf;
+ while (i--) *buf++ = SOX_SIGNED_8BIT_TO_SAMPLE(*buf1++,);
break;
- case 16:
- switch(ft->encoding.encoding) {
- case SOX_ENCODING_SIGN2: read_buf = sw_read_buf; break;
- case SOX_ENCODING_UNSIGNED: read_buf = uw_read_buf; break;
- default:
- lsx_fail_errno(ft,SOX_EFMT,"Do not support this encoding for this data size");
- return 0;
- }
+ }
+ case SND_PCM_FORMAT_U8: {
+ uint8_t * buf1 = (uint8_t *)p->buf;
+ while (i--) *buf++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(*buf1++,);
break;
- default:
- lsx_fail_errno(ft,SOX_EFMT,"Do not support this data size for this handler");
+ }
+ case SND_PCM_FORMAT_S16: {
+ int16_t * buf1 = (int16_t *)p->buf;
+ if (ft->encoding.reverse_bytes) while (i--)
+ *buf++ = SOX_SIGNED_16BIT_TO_SAMPLE(lsx_swapw(*buf1++),);
+ else
+ while (i--) *buf++ = SOX_SIGNED_16BIT_TO_SAMPLE(*buf1++,);
+ break;
+ }
+ case SND_PCM_FORMAT_U16: {
+ uint16_t * buf1 = (uint16_t *)p->buf;
+ if (ft->encoding.reverse_bytes) while (i--)
+ *buf++ = SOX_UNSIGNED_16BIT_TO_SAMPLE(lsx_swapw(*buf1++),);
+ else
+ while (i--) *buf++ = SOX_UNSIGNED_16BIT_TO_SAMPLE(*buf1++,);
+ break;
+ }
+ case SND_PCM_FORMAT_S24: {
+ int24_t * buf1 = (int24_t *)p->buf;
+ while (i--) *buf++ = SOX_SIGNED_24BIT_TO_SAMPLE(*buf1++,);
+ break;
+ }
+ case SND_PCM_FORMAT_U24: {
+ uint24_t * buf1 = (uint24_t *)p->buf;
+ while (i--) *buf++ = SOX_UNSIGNED_24BIT_TO_SAMPLE(*buf1++,);
+ break;
+ }
+ default: lsx_fail_errno(ft, SOX_EFMT, "invalid format");
return 0;
}
+ }
+ return len;
+}
- /* Prevent overflow */
- if (nsamp > alsa->buf_size/(ft->encoding.bits_per_sample >> 3))
- nsamp = (alsa->buf_size/(ft->encoding.bits_per_sample >> 3));
+static size_t write_(sox_format_t * ft, sox_sample_t const * buf, size_t len)
+{
+ priv_t * p = (priv_t *)ft->priv;
+ size_t done, i, n;
+ snd_pcm_sframes_t actual;
+ SOX_SAMPLE_LOCALS;
- len = 0;
- while (len < nsamp) {
- long n = snd_pcm_readi(alsa->pcm_handle, alsa->buf,
- (nsamp - len)/ft->signal.channels); /* ALSA takes "frame" counts. */
- if (n < 0) {
- if (xrun_recovery(alsa->pcm_handle, (int)n) < 0) {
- lsx_fail_errno(ft, SOX_EPERM, "ALSA read error");
- return 0;
- }
- } else {
- n *= ft->signal.channels;
- read_buf(buf + len, alsa->buf, (size_t)n, ft->encoding.reverse_bytes, &ft->clips);
- len += n;
+ for (done = 0; done < len; done += n) {
+ i = n = min(len - done, p->buf_len);
+ switch (p->format) {
+ case SND_PCM_FORMAT_S8: {
+ int8_t * buf1 = (int8_t *)p->buf;
+ while (i--) *buf1++ = SOX_SAMPLE_TO_SIGNED_8BIT(*buf++, ft->clips);
+ break;
}
+ case SND_PCM_FORMAT_U8: {
+ uint8_t * buf1 = (uint8_t *)p->buf;
+ while (i--) *buf1++ = SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf++, ft->clips);
+ break;
+ }
+ case SND_PCM_FORMAT_S16: {
+ int16_t * buf1 = (int16_t *)p->buf;
+ if (ft->encoding.reverse_bytes) while (i--)
+ *buf1++ = lsx_swapw(SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips));
+ else
+ while (i--) *buf1++ = SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips);
+ break;
+ }
+ case SND_PCM_FORMAT_U16: {
+ uint16_t * buf1 = (uint16_t *)p->buf;
+ if (ft->encoding.reverse_bytes) while (i--)
+ *buf1++ = lsx_swapw(SOX_SAMPLE_TO_UNSIGNED_16BIT(*buf++, ft->clips));
+ else
+ while (i--) *buf1++ = SOX_SAMPLE_TO_UNSIGNED_16BIT(*buf++, ft->clips);
+ break;
+ }
+ case SND_PCM_FORMAT_S24: {
+ int24_t * buf1 = (int24_t *)p->buf;
+ while (i--) *buf1++ = SOX_SAMPLE_TO_SIGNED_24BIT(*buf++, ft->clips);
+ break;
+ }
+ case SND_PCM_FORMAT_U24: {
+ uint24_t * buf1 = (uint24_t *)p->buf;
+ while (i--) *buf1++ = SOX_SAMPLE_TO_UNSIGNED_24BIT(*buf++, ft->clips);
+ break;
+ }
}
- return len;
+ for (i = 0; i < n; i += actual * ft->signal.channels) do {
+ actual = snd_pcm_writei(
+ p->pcm, p->buf + i * NBYTES, (n - i) / ft->signal.channels);
+ if (errno == EAGAIN) /* Happens naturally; don't report it: */
+ errno = 0;
+ if (actual < 0 && recover(ft, p->pcm, actual) < 0)
+ return 0;
+ } while (actual < 0);
+ }
+ return len;
}
-static int stopread(sox_format_t * ft)
+static int stop(sox_format_t * ft)
{
- priv_t * alsa = (priv_t *)ft->priv;
-
- snd_pcm_close(alsa->pcm_handle);
-
- free(alsa->buf);
-
- return SOX_SUCCESS;
+ priv_t * p = (priv_t *)ft->priv;
+ snd_pcm_close(p->pcm);
+ free(p->buf);
+ return SOX_SUCCESS;
}
-static int startwrite(sox_format_t * ft)
+static int stop_write(sox_format_t * ft)
{
- return setup(ft, SND_PCM_STREAM_PLAYBACK);
-}
-
-static void sox_ub_write_buf(char* buf1, sox_sample_t const * buf2, size_t len, sox_bool swap UNUSED, size_t * clips)
-{
- SOX_SAMPLE_LOCALS;
- while (len--)
- *(uint8_t *)buf1++ = SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf2++, *clips);
-}
-
-static void sox_sb_write_buf(char *buf1, sox_sample_t const * buf2, size_t len, sox_bool swap UNUSED, size_t * clips)
-{
- SOX_SAMPLE_LOCALS;
- while (len--)
- *(int8_t *)buf1++ = SOX_SAMPLE_TO_SIGNED_8BIT(*buf2++, *clips);
-}
-
-static void sox_uw_write_buf(char *buf1, sox_sample_t const * buf2, size_t len, sox_bool swap, size_t * clips)
-{
- while (len--)
- {
- SOX_SAMPLE_LOCALS;
- uint16_t datum = SOX_SAMPLE_TO_UNSIGNED_16BIT(*buf2++, *clips);
- if (swap)
- datum = lsx_swapw(datum);
- *(uint16_t *)buf1 = datum;
- buf1++; buf1++;
- }
-}
-
-static void sox_sw_write_buf(char *buf1, sox_sample_t const * buf2, size_t len, sox_bool swap, size_t * clips)
-{
- while (len--)
- {
- SOX_SAMPLE_LOCALS;
- int16_t datum = SOX_SAMPLE_TO_SIGNED_16BIT(*buf2++, *clips);
- if (swap)
- datum = lsx_swapw(datum);
- *(int16_t *)buf1 = datum;
- buf1++; buf1++;
- }
-}
-
-static size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, size_t nsamp)
-{
- size_t osamp, done;
- priv_t * alsa = (priv_t *)ft->priv;
- void (*write_buf)(char *, const sox_sample_t *, size_t, sox_bool, size_t *) = 0;
-
- switch(ft->encoding.bits_per_sample) {
- case 8:
- switch (ft->encoding.encoding)
- {
- case SOX_ENCODING_SIGN2:
- write_buf = sox_sb_write_buf;
- break;
- case SOX_ENCODING_UNSIGNED:
- write_buf = sox_ub_write_buf;
- break;
- default:
- lsx_fail_errno(ft,SOX_EFMT,"this encoding is not supported for this data size");
- return 0;
- }
- break;
- case 16:
- switch (ft->encoding.encoding)
- {
- case SOX_ENCODING_SIGN2:
- write_buf = sox_sw_write_buf;
- break;
- case SOX_ENCODING_UNSIGNED:
- write_buf = sox_uw_write_buf;
- break;
- default:
- lsx_fail_errno(ft,SOX_EFMT,"this encoding is not supported for this data size");
- return 0;
- }
- break;
- default:
- lsx_fail_errno(ft,SOX_EFMT,"this data size is not supported by this handler");
- return 0;
- }
-
- for (done = 0; done < nsamp; done += osamp) {
- int err;
- size_t len;
-
- osamp = min(nsamp - done, alsa->buf_size / (ft->encoding.bits_per_sample >> 3));
- write_buf(alsa->buf, buf, osamp, ft->encoding.reverse_bytes, &ft->clips);
- buf += osamp;
-
- for (len = 0; len < osamp;) {
- err = snd_pcm_writei(alsa->pcm_handle,
- alsa->buf + (len * (ft->encoding.bits_per_sample >> 3)),
- (osamp - len) / ft->signal.channels);
- if (errno == EAGAIN) /* Happens naturally; don't report it */
- errno = 0;
- if (err < 0) {
- if (xrun_recovery(alsa->pcm_handle, err) < 0) {
- lsx_fail_errno(ft, SOX_EPERM, "ALSA write error");
- return 0;
- }
- } else
- len += err * ft->signal.channels;
- }
- }
-
- /* keep track of how many frames have been played this period, so we know
- * how many frames of silence to append at the end of playback */
- alsa->frames_this_period = (alsa->frames_this_period + nsamp / ft->signal.channels) % alsa->period_size;
- return nsamp;
-}
-
-
-static int stopwrite(sox_format_t * ft)
-{
- priv_t * alsa = (priv_t *)ft->priv;
-
- /* Pad to hardware period: */
- size_t npad = (alsa->period_size - alsa->frames_this_period) * ft->signal.channels;
+ priv_t * p = (priv_t *)ft->priv;
+ size_t n = ft->signal.channels * p->period, npad = n - (ft->olength % n);
sox_sample_t * buf = lsx_calloc(npad, sizeof(*buf)); /* silent samples */
- write_samples(ft, buf, npad);
- free(buf);
- snd_pcm_drain(alsa->pcm_handle);
- snd_pcm_close(alsa->pcm_handle);
- free(alsa->buf);
- return SOX_SUCCESS;
+ if (npad != n) /* pad to hardware period: */
+ write_(ft, buf, npad);
+ free(buf);
+ snd_pcm_drain(p->pcm);
+ return stop(ft);
}
SOX_FORMAT_HANDLER(alsa)
{
- static char const * const names[] = { "alsa", NULL };
+ static char const * const names[] = {"alsa", NULL};
static unsigned const write_encodings[] = {
- SOX_ENCODING_SIGN2, 16, 8, 0,
- SOX_ENCODING_UNSIGNED, 16, 8, 0,
- 0};
+ SOX_ENCODING_SIGN2, 24, 16, 8, 0, SOX_ENCODING_UNSIGNED, 24, 16, 8, 0, 0};
static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
"Advanced Linux Sound Architecture device driver",
names, SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
- startread, read_samples, stopread,
- startwrite, write_samples, stopwrite,
+ setup, read_, stop, setup, write_, stop_write,
NULL, write_encodings, NULL, sizeof(priv_t)
};
return &handler;
--- a/src/ao.c
+++ b/src/ao.c
@@ -35,7 +35,6 @@
{
priv_t * ao = (priv_t *)ft->priv;
- lsx_set_signal_defaults(&ft->signal);
ao->buf_size = sox_globals.bufsiz - (sox_globals.bufsiz % (ft->encoding.bits_per_sample >> 3));
ao->buf_size *= (ft->encoding.bits_per_sample >> 3);
ao->buf = lsx_malloc(ao->buf_size);
--- a/src/formats.c
+++ b/src/formats.c
@@ -477,6 +477,9 @@
else sox_init_encodinginfo(&ft->encoding);
set_endiannesses(ft);
+ if ((ft->handler.flags & SOX_FILE_DEVICE) && !(ft->handler.flags & SOX_FILE_PHONY))
+ lsx_set_signal_defaults(ft);
+
ft->priv = lsx_calloc(1, ft->handler.priv_size);
/* Read and write starters can change their formats. */
if (ft->handler.startread && (*ft->handler.startread)(ft) != SOX_SUCCESS) {
@@ -485,15 +488,24 @@
}
/* Fill in some defaults: */
- if (!ft->signal.precision)
+ if (sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample))
ft->signal.precision = sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample);
if (!(ft->handler.flags & SOX_FILE_PHONY) && !ft->signal.channels)
ft->signal.channels = 1;
- if (sox_checkformat(ft) == SOX_SUCCESS)
- return ft;
- lsx_fail("bad input format for %s `%s': %s", type, ft->filename, ft->sox_errstr);
+ if (sox_checkformat(ft) != SOX_SUCCESS) {
+ lsx_fail("bad input format for %s `%s': %s", type, ft->filename, ft->sox_errstr);
+ goto error;
+ }
+ if (signal) {
+ if (signal->rate && signal->rate != ft->signal.rate)
+ lsx_warn("can't set sample rate %g; using %g", signal->rate, ft->signal.rate);
+ if (signal->channels && signal->channels != ft->signal.channels)
+ lsx_warn("can't set %u channels; using %u", signal->channels, ft->signal.channels);
+ }
+ return ft;
+
error:
if (ft->fp && ft->fp != stdin)
xfclose(ft->fp, ft->io_type);
@@ -806,9 +818,18 @@
goto error;
}
- if (sox_checkformat(ft) == SOX_SUCCESS)
- return ft;
- lsx_fail("bad format for output file `%s': %s", ft->filename, ft->sox_errstr);
+ if (sox_checkformat(ft) != SOX_SUCCESS) {
+ lsx_fail("bad format for output file `%s': %s", ft->filename, ft->sox_errstr);
+ goto error;
+ }
+
+ if ((ft->handler.flags & SOX_FILE_DEVICE) && signal) {
+ if (signal->rate && signal->rate != ft->signal.rate)
+ lsx_warn("can't set sample rate %g; using %g", signal->rate, ft->signal.rate);
+ if (signal->channels && signal->channels != ft->signal.channels)
+ lsx_warn("can't set %u channels; using %u", signal->channels, ft->signal.channels);
+ }
+ return ft;
error:
if (ft->fp && ft->fp != stdout)
--- a/src/formats_i.c
+++ b/src/formats_i.c
@@ -39,11 +39,16 @@
ft->sox_errstr[255] = '\0';
}
-void lsx_set_signal_defaults(sox_signalinfo_t * signal)
+void lsx_set_signal_defaults(sox_format_t * ft)
{
- if (!signal->rate ) signal->rate = SOX_DEFAULT_RATE;
- if (!signal->precision) signal->precision = SOX_DEFAULT_PRECISION;
- if (!signal->channels ) signal->channels = SOX_DEFAULT_CHANNELS;
+ if (!ft->signal.rate ) ft->signal.rate = SOX_DEFAULT_RATE;
+ if (!ft->signal.precision) ft->signal.precision = SOX_DEFAULT_PRECISION;
+ if (!ft->signal.channels ) ft->signal.channels = SOX_DEFAULT_CHANNELS;
+
+ if (!ft->encoding.bits_per_sample)
+ ft->encoding.bits_per_sample = ft->signal.precision;
+ if (ft->encoding.encoding == SOX_ENCODING_UNKNOWN)
+ ft->encoding.encoding = SOX_ENCODING_SIGN2;
}
int lsx_check_read_params(sox_format_t * ft, unsigned channels,
--- a/src/oss.c
+++ b/src/oss.c
@@ -45,9 +45,7 @@
int sampletype, samplesize, dsp_stereo;
int tmp, rc;
priv_t *file = (priv_t *)ft->priv;
- sox_signalinfo_t client_signal = ft->signal;
- lsx_set_signal_defaults(&ft->signal);
if (ft->encoding.bits_per_sample == 8) {
sampletype = AFMT_U8;
samplesize = 8;
@@ -156,11 +154,7 @@
}
if (tmp != dsp_stereo)
- {
- if (client_signal.channels != 0)
- lsx_warn("Sound card appears to support only %d channels. Overriding format", tmp+1);
ft->signal.channels = tmp + 1;
- }
tmp = ft->signal.rate;
if (ioctl (fileno(ft->fp), SNDCTL_DSP_SPEED, &tmp) < 0 ||
@@ -174,12 +168,8 @@
* we can't hear anyways.
*/
if ((int)ft->signal.rate - tmp > (tmp * .01) ||
- tmp - (int)ft->signal.rate > (tmp * .01)) {
- if (client_signal.rate != 0)
- lsx_warn("Unable to set audio speed to %g (set to %d)",
- ft->signal.rate, tmp);
+ tmp - (int)ft->signal.rate > (tmp * .01))
ft->signal.rate = tmp;
- }
}
/* Find out block size to use last because the driver could compute
--- a/src/sox.c
+++ b/src/sox.c
@@ -2631,9 +2631,13 @@
f->volume = 1.0 / sqrt((double)input_count);
if (sox_mode == sox_rec && !j) { /* Set the recording parameters: */
- if (input_count > 1) /* from the (just openned) next */
+ if (input_count > 1) { /* from the (just openned) next */
f->signal = files[1]->ft->signal; /* input file, or from the output */
- else f->signal = files[1]->signal; /* file (which is not open yet). */
+ f->encoding = files[1]->ft->encoding;
+ } else {
+ f->signal = files[1]->signal; /* file (which is not open yet). */
+ f->encoding = files[1]->encoding;
+ }
}
files[j]->ft = sox_open_read(f->filename, &f->signal, &f->encoding, f->filetype);
if (!files[j]->ft)
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -139,7 +139,7 @@
size_t lsx_writebuf(sox_format_t * ft, void const *buf, size_t len);
int lsx_reads(sox_format_t * ft, char *c, size_t len);
int lsx_writes(sox_format_t * ft, char const * c);
-void lsx_set_signal_defaults(sox_signalinfo_t * signal);
+void lsx_set_signal_defaults(sox_format_t * ft);
#define lsx_writechars(ft, chars, len) (lsx_writebuf(ft, chars, len) == len? SOX_SUCCESS : SOX_EOF)
size_t lsx_read_3_buf(sox_format_t * ft, uint24_t *buf, size_t len);
--- a/src/sunaudio.c
+++ b/src/sunaudio.c
@@ -188,8 +188,6 @@
#endif
char simple_hw=0;
- lsx_set_signal_defaults(&ft->signal);
-
/* Hard-code for now. */
file->count = 0;
file->pos = 0;