1 /*
2  * libsndio sound handler
3  *
4  * Copyright (c) 2009 Alexandre Ratchov <alex@caoua.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #include "sox_i.h"
19 #include <string.h>
20 #include <sndio.h>
21 
22 struct sndio_priv {
23   struct sio_hdl *hdl;             /* handle to speak to libsndio */
24   struct sio_par par;              /* current device parameters */
25 #define SNDIO_BUFSZ 0x1000
26   unsigned char buf[SNDIO_BUFSZ];  /* temp buffer for converions */
27 };
28 
29 /*
30  * convert ``count'' samples from sox encoding to sndio encoding
31  */
encode(struct sio_par * par,sox_sample_t const * idata,unsigned char * odata,unsigned count)32 static void encode(struct sio_par *par,
33     sox_sample_t const *idata, unsigned char *odata, unsigned count)
34 {
35   int obnext, osnext, s, osigbit;
36   unsigned oshift, obps, i;
37 
38   obps = par->bps;
39   osigbit = par->sig ? 0 : 1 << (par->bits - 1);
40   oshift = 32 - (par->msb ? par->bps * 8 : par->bits);
41   if (par->le) {
42     obnext = 1;
43     osnext = 0;
44   } else {
45     odata += par->bps - 1;
46     obnext = -1;
47     osnext = 2 * par->bps;
48   }
49   for (; count > 0; count--) {
50     s = (*idata++ >> oshift) ^ osigbit;
51     for (i = obps; i > 0; i--) {
52       *odata = (unsigned char)s;
53       s >>= 8;
54       odata += obnext;
55     }
56     odata += osnext;
57   }
58 }
59 
60 /*
61  * convert ``count'' samples from sndio encoding to sox encoding
62  */
decode(struct sio_par * par,unsigned char * idata,sox_sample_t * odata,unsigned count)63 static void decode(struct sio_par *par,
64     unsigned char *idata, sox_sample_t *odata, unsigned count)
65 {
66   unsigned ishift, ibps, i;
67   int s = 0xdeadbeef, ibnext, isnext, isigbit;
68 
69   ibps = par->bps;
70   isigbit = par->sig ? 0 : 1 << (par->bits - 1);
71   ishift = 32 - (par->msb ? par->bps * 8 : par->bits);
72   if (par->le) {
73     idata += par->bps - 1;
74     ibnext = -1;
75     isnext = 2 * par->bps;
76   } else {
77     ibnext = 1;
78     isnext = 0;
79   }
80   for (; count > 0; count--) {
81     for (i = ibps; i > 0; i--) {
82       s <<= 8;
83       s |= *idata;
84       idata += ibnext;
85     }
86     idata += isnext;
87     *odata++ = (s ^ isigbit) << ishift;
88   }
89 }
90 
startany(sox_format_t * ft,unsigned mode)91 static int startany(sox_format_t *ft, unsigned mode)
92 {
93   struct sndio_priv *p = (struct sndio_priv *)ft->priv;
94   struct sio_par reqpar;
95   char *device;
96 
97   device = ft->filename;
98   if (strcmp("default", device) == 0)
99     device = NULL;
100 
101   p->hdl = sio_open(device, mode, 0);
102   if (p->hdl == NULL)
103     return SOX_EOF;
104   /*
105    * set specified parameters, leaving others to the defaults
106    */
107   sio_initpar(&reqpar);
108   if (ft->signal.rate > 0)
109     reqpar.rate = ft->signal.rate;
110   if (ft->signal.channels > 0) {
111     if (mode == SIO_PLAY)
112       reqpar.pchan = ft->signal.channels;
113     else
114       reqpar.rchan = ft->signal.channels;
115   }
116   switch (ft->encoding.encoding) {
117   case SOX_ENCODING_SIGN2:
118     reqpar.sig = 1;
119     break;
120   case SOX_ENCODING_UNSIGNED:
121     reqpar.sig = 0;
122     break;
123   default:
124     break;  /* use device default */
125   }
126   if (ft->encoding.bits_per_sample > 0)
127     reqpar.bits = ft->encoding.bits_per_sample;
128   else if (ft->signal.precision > 0)
129     reqpar.bits = ft->signal.precision;
130   else
131     reqpar.bits = SOX_DEFAULT_PRECISION;
132   reqpar.bps = (reqpar.bits + 7) / 8;
133   reqpar.msb = 1;
134   if (ft->encoding.reverse_bytes != sox_option_default) {
135     reqpar.le = SIO_LE_NATIVE;
136     if (ft->encoding.reverse_bytes)
137       reqpar.le = !reqpar.le;
138   }
139   if (!sio_setpar(p->hdl, &reqpar) ||
140       !sio_getpar(p->hdl, &p->par))
141     goto failed;
142   ft->signal.channels = (mode == SIO_PLAY) ? p->par.pchan : p->par.rchan;
143   ft->signal.precision = p->par.bits;
144   ft->signal.rate = p->par.rate;
145   ft->encoding.encoding = p->par.sig ? SOX_ENCODING_SIGN2 : SOX_ENCODING_UNSIGNED;
146   ft->encoding.bits_per_sample = p->par.bps * 8;
147   ft->encoding.reverse_bytes = SIO_LE_NATIVE ? !p->par.le : p->par.le;
148   ft->encoding.reverse_nibbles = sox_option_no;
149   ft->encoding.reverse_bits = sox_option_no;
150 
151   if (!sio_start(p->hdl))
152     goto failed;
153   return SOX_SUCCESS;
154  failed:
155   sio_close(p->hdl);
156   return SOX_EOF;
157 }
158 
stopany(sox_format_t * ft)159 static int stopany(sox_format_t *ft)
160 {
161   sio_close(((struct sndio_priv *)ft->priv)->hdl);
162   return SOX_SUCCESS;
163 }
164 
startread(sox_format_t * ft)165 static int startread(sox_format_t *ft)
166 {
167   return startany(ft, SIO_REC);
168 }
169 
startwrite(sox_format_t * ft)170 static int startwrite(sox_format_t *ft)
171 {
172   return startany(ft, SIO_PLAY);
173 }
174 
readsamples(sox_format_t * ft,sox_sample_t * buf,size_t len)175 static size_t readsamples(sox_format_t *ft, sox_sample_t *buf, size_t len)
176 {
177   struct sndio_priv *p = (struct sndio_priv *)ft->priv;
178   unsigned char partial[4];
179   unsigned cpb, cc, pc;
180   size_t todo, n;
181 
182   pc = 0;
183   todo = len * p->par.bps;
184   cpb = SNDIO_BUFSZ - (SNDIO_BUFSZ % p->par.bps);
185   while (todo > 0) {
186     memcpy(p->buf, partial, (size_t)pc);
187     cc = cpb - pc;
188     if (cc > todo)
189       cc = todo;
190     n = sio_read(p->hdl, p->buf + pc, (size_t)cc);
191     if (n == 0 && sio_eof(p->hdl))
192       break;
193     n += pc;
194     pc = n % p->par.bps;
195     n -= pc;
196     memcpy(partial, p->buf + n, (size_t)pc);
197     decode(&p->par, p->buf, buf, (unsigned)(n / p->par.bps));
198     buf += n / p->par.bps;
199     todo -= n;
200   }
201   return len - todo / p->par.bps;
202 }
203 
writesamples(sox_format_t * ft,const sox_sample_t * buf,size_t len)204 static size_t writesamples(sox_format_t *ft, const sox_sample_t *buf, size_t len)
205 {
206   struct sndio_priv *p = (struct sndio_priv *)ft->priv;
207   unsigned sc, spb;
208   size_t n, todo;
209 
210   todo = len;
211   spb = SNDIO_BUFSZ / p->par.bps;
212   while (todo > 0) {
213     sc = spb;
214     if (sc > todo)
215       sc = todo;
216     encode(&p->par, buf, p->buf, sc);
217     n = sio_write(p->hdl, p->buf, (size_t)(sc * p->par.bps));
218     if (n == 0 && sio_eof(p->hdl))
219       break;
220     n /= p->par.bps;
221     todo -= n;
222     buf += n;
223   }
224   return len - todo;
225 }
226 
LSX_FORMAT_HANDLER(sndio)227 LSX_FORMAT_HANDLER(sndio)
228 {
229   static char const * const names[] = {"sndio", NULL};
230   static unsigned const write_encodings[] = {
231     SOX_ENCODING_SIGN2, 32, 24, 16, 8, 0,
232     SOX_ENCODING_UNSIGNED, 32, 24, 16, 8, 0,
233     0
234   };
235   static sox_format_handler_t const handler = {
236     SOX_LIB_VERSION_CODE,
237     "libsndio device driver",
238     names,
239     SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
240     startread, readsamples, stopany,
241     startwrite, writesamples, stopany,
242     NULL, write_encodings, NULL,
243     sizeof(struct sndio_priv)
244   };
245   return &handler;
246 }
247