wav: simplify extended fmt chunk handling [bug #198]
[sox.git] / src / alsa.c
blobdaf040a5448a5dbaf1b9a5b55919782a8ffb5ed5
1 /* libSoX device driver: ALSA (c) 2006-2012 SoX contributors
3 * This library is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License as published by
5 * the Free Software Foundation; either version 2.1 of the License, or (at
6 * your option) any later version.
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11 * General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 #include "sox_i.h"
19 #include <alsa/asoundlib.h>
21 typedef struct {
22 snd_pcm_uframes_t buf_len, period;
23 snd_pcm_t * pcm;
24 char * buf;
25 unsigned int format;
26 } priv_t;
28 static const
29 struct {
30 unsigned int bits;
31 enum _snd_pcm_format alsa_fmt;
32 unsigned int bytes; /* occupied in the buffer per sample */
33 sox_encoding_t enc;
34 } formats[] = {
35 /* order by # of bits; within that, preferred first */
36 { 8, SND_PCM_FORMAT_S8, 1, SOX_ENCODING_SIGN2 },
37 { 8, SND_PCM_FORMAT_U8, 1, SOX_ENCODING_UNSIGNED },
38 { 16, SND_PCM_FORMAT_S16, 2, SOX_ENCODING_SIGN2 },
39 { 16, SND_PCM_FORMAT_U16, 2, SOX_ENCODING_UNSIGNED },
40 { 24, SND_PCM_FORMAT_S24, 4, SOX_ENCODING_SIGN2 },
41 { 24, SND_PCM_FORMAT_U24, 4, SOX_ENCODING_UNSIGNED },
42 { 24, SND_PCM_FORMAT_S24_3LE, 3, SOX_ENCODING_SIGN2 },
43 { 32, SND_PCM_FORMAT_S32, 4, SOX_ENCODING_SIGN2 },
44 { 32, SND_PCM_FORMAT_U32, 4, SOX_ENCODING_UNSIGNED },
45 { 0, 0, 0, SOX_ENCODING_UNKNOWN } /* end of list */
48 static int select_format(
49 sox_encoding_t * encoding_,
50 unsigned * nbits_,
51 snd_pcm_format_mask_t const * mask,
52 unsigned int * format)
54 unsigned int from = 0, to; /* NB: "to" actually points one after the last */
55 int cand = -1;
57 while (formats[from].bits < *nbits_ && formats[from].bits != 0)
58 from++; /* find the first entry with at least *nbits_ bits */
59 for (to = from; formats[to].bits != 0; to++) ; /* find end of list */
61 while (to > 0) {
62 unsigned int i, bits_next = 0;
63 for (i = from; i < to; i++) {
64 lsx_debug_most("select_format: trying #%u", i);
65 if (snd_pcm_format_mask_test(mask, formats[i].alsa_fmt)) {
66 if (formats[i].enc == *encoding_) {
67 cand = i;
68 break; /* found a match */
69 } else if (cand == -1) /* don't overwrite a candidate that
70 was earlier in the list */
71 cand = i; /* will work, but encoding differs */
74 if (cand != -1)
75 break;
76 /* no candidate found yet; now try formats with less bits: */
77 to = from;
78 if (from > 0)
79 bits_next = formats[from-1].bits;
80 while (from && formats[from-1].bits == bits_next)
81 from--; /* go back to the first entry with bits_next bits */
84 if (cand == -1) {
85 lsx_debug("select_format: no suitable ALSA format found");
86 return -1;
89 if (*nbits_ != formats[cand].bits || *encoding_ != formats[cand].enc) {
90 lsx_warn("can't encode %u-bit %s", *nbits_,
91 sox_encodings_info[*encoding_].desc);
92 *nbits_ = formats[cand].bits;
93 *encoding_ = formats[cand].enc;
95 lsx_debug("selecting format %d: %s (%s)", cand,
96 snd_pcm_format_name(formats[cand].alsa_fmt),
97 snd_pcm_format_description(formats[cand].alsa_fmt));
98 *format = cand;
99 return 0;
102 #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)
103 static int setup(sox_format_t * ft)
105 priv_t * p = (priv_t *)ft->priv;
106 snd_pcm_hw_params_t * params = NULL;
107 snd_pcm_format_mask_t * mask = NULL;
108 snd_pcm_uframes_t min, max;
109 unsigned n;
110 int err;
112 _(snd_pcm_open, (&p->pcm, ft->filename, ft->mode == 'r'? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, 0));
113 _(snd_pcm_hw_params_malloc, (&params));
114 _(snd_pcm_hw_params_any, (p->pcm, params));
115 #if SND_LIB_VERSION >= 0x010009 /* Disable alsa-lib resampling: */
116 _(snd_pcm_hw_params_set_rate_resample, (p->pcm, params, 0));
117 #endif
118 _(snd_pcm_hw_params_set_access, (p->pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED));
120 _(snd_pcm_format_mask_malloc, (&mask)); /* Set format: */
121 snd_pcm_hw_params_get_format_mask(params, mask);
122 _(select_format, (&ft->encoding.encoding, &ft->encoding.bits_per_sample, mask, &p->format));
123 _(snd_pcm_hw_params_set_format, (p->pcm, params, formats[p->format].alsa_fmt));
124 snd_pcm_format_mask_free(mask), mask = NULL;
126 n = ft->signal.rate; /* Set rate: */
127 _(snd_pcm_hw_params_set_rate_near, (p->pcm, params, &n, 0));
128 ft->signal.rate = n;
130 n = ft->signal.channels; /* Set channels: */
131 _(snd_pcm_hw_params_set_channels_near, (p->pcm, params, &n));
132 ft->signal.channels = n;
134 /* Get number of significant bits: */
135 if ((err = snd_pcm_hw_params_get_sbits(params)) > 0)
136 ft->signal.precision = min(err, SOX_SAMPLE_PRECISION);
137 else lsx_debug("snd_pcm_hw_params_get_sbits can't tell precision: %s",
138 snd_strerror(err));
140 /* Set buf_len > > sox_globals.bufsiz for no underrun: */
141 p->buf_len = sox_globals.bufsiz * 8 / formats[p->format].bytes /
142 ft->signal.channels;
143 _(snd_pcm_hw_params_get_buffer_size_min, (params, &min));
144 _(snd_pcm_hw_params_get_buffer_size_max, (params, &max));
145 p->period = range_limit(p->buf_len, min, max) / 8;
146 p->buf_len = p->period * 8;
147 _(snd_pcm_hw_params_set_period_size_near, (p->pcm, params, &p->period, 0));
148 _(snd_pcm_hw_params_set_buffer_size_near, (p->pcm, params, &p->buf_len));
149 if (p->period * 2 > p->buf_len) {
150 lsx_fail_errno(ft, SOX_EPERM, "buffer too small");
151 goto error;
154 _(snd_pcm_hw_params, (p->pcm, params)); /* Configure ALSA */
155 snd_pcm_hw_params_free(params), params = NULL;
156 _(snd_pcm_prepare, (p->pcm));
157 p->buf_len *= ft->signal.channels; /* No longer in `frames' */
158 p->buf = lsx_malloc(p->buf_len * formats[p->format].bytes);
159 return SOX_SUCCESS;
161 error:
162 if (mask) snd_pcm_format_mask_free(mask);
163 if (params) snd_pcm_hw_params_free(params);
164 return SOX_EOF;
167 static int recover(sox_format_t * ft, snd_pcm_t * pcm, int err)
169 if (err == -EPIPE)
170 lsx_warn("%s-run", ft->mode == 'r'? "over" : "under");
171 else if (err != -ESTRPIPE)
172 lsx_warn("%s", snd_strerror(err));
173 else while ((err = snd_pcm_resume(pcm)) == -EAGAIN) {
174 lsx_report("suspended");
175 sleep(1); /* Wait until the suspend flag is released */
177 if (err < 0 && (err = snd_pcm_prepare(pcm)) < 0)
178 lsx_fail_errno(ft, SOX_EPERM, "%s", snd_strerror(err));
179 return err;
182 static size_t read_(sox_format_t * ft, sox_sample_t * buf, size_t len)
184 priv_t * p = (priv_t *)ft->priv;
185 snd_pcm_sframes_t i, n;
186 size_t done;
188 len = min(len, p->buf_len);
189 for (done = 0; done < len; done += n) {
190 do {
191 n = snd_pcm_readi(p->pcm, p->buf, (len - done) / ft->signal.channels);
192 if (n < 0 && recover(ft, p->pcm, (int)n) < 0)
193 return 0;
194 } while (n <= 0);
196 i = n *= ft->signal.channels;
197 switch (formats[p->format].alsa_fmt) {
198 case SND_PCM_FORMAT_S8: {
199 int8_t * buf1 = (int8_t *)p->buf;
200 while (i--) *buf++ = SOX_SIGNED_8BIT_TO_SAMPLE(*buf1++,);
201 break;
203 case SND_PCM_FORMAT_U8: {
204 uint8_t * buf1 = (uint8_t *)p->buf;
205 while (i--) *buf++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(*buf1++,);
206 break;
208 case SND_PCM_FORMAT_S16: {
209 int16_t * buf1 = (int16_t *)p->buf;
210 if (ft->encoding.reverse_bytes) while (i--)
211 *buf++ = SOX_SIGNED_16BIT_TO_SAMPLE(lsx_swapw(*buf1++),);
212 else
213 while (i--) *buf++ = SOX_SIGNED_16BIT_TO_SAMPLE(*buf1++,);
214 break;
216 case SND_PCM_FORMAT_U16: {
217 uint16_t * buf1 = (uint16_t *)p->buf;
218 if (ft->encoding.reverse_bytes) while (i--)
219 *buf++ = SOX_UNSIGNED_16BIT_TO_SAMPLE(lsx_swapw(*buf1++),);
220 else
221 while (i--) *buf++ = SOX_UNSIGNED_16BIT_TO_SAMPLE(*buf1++,);
222 break;
224 case SND_PCM_FORMAT_S24: {
225 sox_int24_t * buf1 = (sox_int24_t *)p->buf;
226 while (i--) *buf++ = SOX_SIGNED_24BIT_TO_SAMPLE(*buf1++,);
227 break;
229 case SND_PCM_FORMAT_S24_3LE: {
230 unsigned char *buf1 = (unsigned char *)p->buf;
231 while (i--) {
232 uint32_t temp;
233 temp = *buf1++;
234 temp |= *buf1++ << 8;
235 temp |= *buf1++ << 16;
236 *buf++ = SOX_SIGNED_24BIT_TO_SAMPLE((sox_int24_t)temp,);
238 break;
240 case SND_PCM_FORMAT_U24: {
241 sox_uint24_t * buf1 = (sox_uint24_t *)p->buf;
242 while (i--) *buf++ = SOX_UNSIGNED_24BIT_TO_SAMPLE(*buf1++,);
243 break;
245 case SND_PCM_FORMAT_S32: {
246 int32_t * buf1 = (int32_t *)p->buf;
247 while (i--) *buf++ = SOX_SIGNED_32BIT_TO_SAMPLE(*buf1++,);
248 break;
250 case SND_PCM_FORMAT_U32: {
251 uint32_t * buf1 = (uint32_t *)p->buf;
252 while (i--) *buf++ = SOX_UNSIGNED_32BIT_TO_SAMPLE(*buf1++,);
253 break;
255 default: lsx_fail_errno(ft, SOX_EFMT, "invalid format");
256 return 0;
259 return len;
262 static size_t write_(sox_format_t * ft, sox_sample_t const * buf, size_t len)
264 priv_t * p = (priv_t *)ft->priv;
265 size_t done, i, n;
266 snd_pcm_sframes_t actual;
267 SOX_SAMPLE_LOCALS;
269 for (done = 0; done < len; done += n) {
270 i = n = min(len - done, p->buf_len);
271 switch (formats[p->format].alsa_fmt) {
272 case SND_PCM_FORMAT_S8: {
273 int8_t * buf1 = (int8_t *)p->buf;
274 while (i--) *buf1++ = SOX_SAMPLE_TO_SIGNED_8BIT(*buf++, ft->clips);
275 break;
277 case SND_PCM_FORMAT_U8: {
278 uint8_t * buf1 = (uint8_t *)p->buf;
279 while (i--) *buf1++ = SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf++, ft->clips);
280 break;
282 case SND_PCM_FORMAT_S16: {
283 int16_t * buf1 = (int16_t *)p->buf;
284 if (ft->encoding.reverse_bytes) while (i--)
285 *buf1++ = lsx_swapw(SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips));
286 else
287 while (i--) *buf1++ = SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips);
288 break;
290 case SND_PCM_FORMAT_U16: {
291 uint16_t * buf1 = (uint16_t *)p->buf;
292 if (ft->encoding.reverse_bytes) while (i--)
293 *buf1++ = lsx_swapw(SOX_SAMPLE_TO_UNSIGNED_16BIT(*buf++, ft->clips));
294 else
295 while (i--) *buf1++ = SOX_SAMPLE_TO_UNSIGNED_16BIT(*buf++, ft->clips);
296 break;
298 case SND_PCM_FORMAT_S24: {
299 sox_int24_t * buf1 = (sox_int24_t *)p->buf;
300 while (i--) *buf1++ = SOX_SAMPLE_TO_SIGNED_24BIT(*buf++, ft->clips);
301 break;
303 case SND_PCM_FORMAT_S24_3LE: {
304 unsigned char *buf1 = (unsigned char *)p->buf;
305 while (i--) {
306 uint32_t temp = (uint32_t)SOX_SAMPLE_TO_SIGNED_24BIT(*buf++, ft->clips);
307 *buf1++ = (temp & 0x000000FF);
308 *buf1++ = (temp & 0x0000FF00) >> 8;
309 *buf1++ = (temp & 0x00FF0000) >> 16;
311 break;
313 case SND_PCM_FORMAT_U24: {
314 sox_uint24_t * buf1 = (sox_uint24_t *)p->buf;
315 while (i--) *buf1++ = SOX_SAMPLE_TO_UNSIGNED_24BIT(*buf++, ft->clips);
316 break;
318 case SND_PCM_FORMAT_S32: {
319 int32_t * buf1 = (int32_t *)p->buf;
320 while (i--) *buf1++ = SOX_SAMPLE_TO_SIGNED_32BIT(*buf++, ft->clips);
321 break;
323 case SND_PCM_FORMAT_U32: {
324 uint32_t * buf1 = (uint32_t *)p->buf;
325 while (i--) *buf1++ = SOX_SAMPLE_TO_UNSIGNED_32BIT(*buf++, ft->clips);
326 break;
328 default: lsx_fail_errno(ft, SOX_EFMT, "invalid format");
329 return 0;
331 for (i = 0; i < n; i += actual * ft->signal.channels) do {
332 actual = snd_pcm_writei(p->pcm,
333 p->buf + i * formats[p->format].bytes,
334 (n - i) / ft->signal.channels);
335 if (errno == EAGAIN) /* Happens naturally; don't report it: */
336 errno = 0;
337 if (actual < 0 && recover(ft, p->pcm, (int)actual) < 0)
338 return 0;
339 } while (actual < 0);
341 return len;
344 static int stop(sox_format_t * ft)
346 priv_t * p = (priv_t *)ft->priv;
347 snd_pcm_close(p->pcm);
348 free(p->buf);
349 return SOX_SUCCESS;
352 static int stop_write(sox_format_t * ft)
354 priv_t * p = (priv_t *)ft->priv;
355 size_t n = ft->signal.channels * p->period, npad = n - (ft->olength % n);
356 sox_sample_t * buf = lsx_calloc(npad, sizeof(*buf)); /* silent samples */
358 if (npad != n) /* pad to hardware period: */
359 write_(ft, buf, npad);
360 free(buf);
361 snd_pcm_drain(p->pcm);
362 return stop(ft);
365 LSX_FORMAT_HANDLER(alsa)
367 static char const * const names[] = {"alsa", NULL};
368 static unsigned const write_encodings[] = {
369 SOX_ENCODING_SIGN2 , 32, 24, 16, 8, 0,
370 SOX_ENCODING_UNSIGNED, 32, 24, 16, 8, 0,
372 static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
373 "Advanced Linux Sound Architecture device driver",
374 names, SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
375 setup, read_, stop, setup, write_, stop_write,
376 NULL, write_encodings, NULL, sizeof(priv_t)
378 return &handler;