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
19 #include <alsa/asoundlib.h>
22 snd_pcm_uframes_t buf_len
, period
;
31 enum _snd_pcm_format alsa_fmt
;
32 unsigned int bytes
; /* occupied in the buffer per sample */
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_
,
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 */
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 */
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_
) {
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 */
76 /* no candidate found yet; now try formats with less bits: */
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 */
85 lsx_debug("select_format: no suitable ALSA format found");
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
));
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
;
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
, (¶ms
));
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));
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));
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",
140 /* Set buf_len > > sox_globals.bufsiz for no underrun: */
141 p
->buf_len
= sox_globals
.bufsiz
* 8 / formats
[p
->format
].bytes
/
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");
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
);
162 if (mask
) snd_pcm_format_mask_free(mask
);
163 if (params
) snd_pcm_hw_params_free(params
);
167 static int recover(sox_format_t
* ft
, snd_pcm_t
* pcm
, int err
)
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
));
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
;
188 len
= min(len
, p
->buf_len
);
189 for (done
= 0; done
< len
; done
+= n
) {
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)
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
++,);
203 case SND_PCM_FORMAT_U8
: {
204 uint8_t * buf1
= (uint8_t *)p
->buf
;
205 while (i
--) *buf
++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(*buf1
++,);
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
++),);
213 while (i
--) *buf
++ = SOX_SIGNED_16BIT_TO_SAMPLE(*buf1
++,);
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
++),);
221 while (i
--) *buf
++ = SOX_UNSIGNED_16BIT_TO_SAMPLE(*buf1
++,);
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
++,);
229 case SND_PCM_FORMAT_S24_3LE
: {
230 unsigned char *buf1
= (unsigned char *)p
->buf
;
234 temp
|= *buf1
++ << 8;
235 temp
|= *buf1
++ << 16;
236 *buf
++ = SOX_SIGNED_24BIT_TO_SAMPLE((sox_int24_t
)temp
,);
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
++,);
245 case SND_PCM_FORMAT_S32
: {
246 int32_t * buf1
= (int32_t *)p
->buf
;
247 while (i
--) *buf
++ = SOX_SIGNED_32BIT_TO_SAMPLE(*buf1
++,);
250 case SND_PCM_FORMAT_U32
: {
251 uint32_t * buf1
= (uint32_t *)p
->buf
;
252 while (i
--) *buf
++ = SOX_UNSIGNED_32BIT_TO_SAMPLE(*buf1
++,);
255 default: lsx_fail_errno(ft
, SOX_EFMT
, "invalid format");
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
;
266 snd_pcm_sframes_t actual
;
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
);
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
);
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
));
287 while (i
--) *buf1
++ = SOX_SAMPLE_TO_SIGNED_16BIT(*buf
++, ft
->clips
);
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
));
295 while (i
--) *buf1
++ = SOX_SAMPLE_TO_UNSIGNED_16BIT(*buf
++, ft
->clips
);
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
);
303 case SND_PCM_FORMAT_S24_3LE
: {
304 unsigned char *buf1
= (unsigned char *)p
->buf
;
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;
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
);
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
);
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
);
328 default: lsx_fail_errno(ft
, SOX_EFMT
, "invalid format");
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: */
337 if (actual
< 0 && recover(ft
, p
->pcm
, (int)actual
) < 0)
339 } while (actual
< 0);
344 static int stop(sox_format_t
* ft
)
346 priv_t
* p
= (priv_t
*)ft
->priv
;
347 snd_pcm_close(p
->pcm
);
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
);
361 snd_pcm_drain(p
->pcm
);
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
)