1 /**
2 * @file sndio.c SndIO sound driver
3 *
4 * Copyright (C) 2014 Creytiv.com
5 */
6 #include <stdlib.h>
7 #include <string.h>
8 #include <sndio.h>
9 #include <pthread.h>
10 #include <re.h>
11 #include <rem.h>
12 #include <baresip.h>
13
14
15 /**
16 * @defgroup sndio sndio
17 *
18 * This module implements audio driver for OpenBSD sndio
19 */
20
21
22 struct ausrc_st {
23 const struct ausrc *as; /* pointer to base-class */
24 struct sio_hdl *hdl;
25 pthread_t thread;
26 int16_t *sampv;
27 size_t sampc;
28 int run;
29 ausrc_read_h *rh;
30 void *arg;
31 };
32
33 struct auplay_st {
34 const struct auplay *ap; /* pointer to base-class */
35 struct sio_hdl *hdl;
36 pthread_t thread;
37 int16_t *sampv;
38 size_t sampc;
39 int run;
40 auplay_write_h *wh;
41 void *arg;
42 };
43
44 static struct ausrc *ausrc;
45 static struct auplay *auplay;
46
47
sndio_initpar(uint32_t srate,uint8_t ch)48 static struct sio_par *sndio_initpar(uint32_t srate, uint8_t ch)
49 {
50 struct sio_par *par = NULL;
51
52 if ((par = mem_zalloc(sizeof(*par), NULL)) == NULL)
53 return NULL;
54
55 sio_initpar(par);
56
57 /* sndio doesn't support a-low and u-low */
58 par->bits = 16;
59 par->bps = SIO_BPS(par->bits);
60 par->sig = 1;
61 par->le = SIO_LE_NATIVE;
62
63 par->rchan = ch;
64 par->pchan = ch;
65 par->rate = srate;
66
67 return par;
68 }
69
70
read_thread(void * arg)71 static void *read_thread(void *arg)
72 {
73 struct ausrc_st *st = arg;
74
75 if (!sio_start(st->hdl)) {
76 warning("sndio: could not start record\n");
77 goto out;
78 }
79
80 while (st->run) {
81 size_t n = sio_read(st->hdl, st->sampv, st->sampc*2);
82 st->rh(st->sampv, n/2, st->arg);
83 }
84
85 out:
86 return NULL;
87 }
88
89
write_thread(void * arg)90 static void *write_thread(void *arg)
91 {
92 struct auplay_st *st = arg;
93
94 if (!sio_start(st->hdl)) {
95 warning("sndio: could not start playback\n");
96 goto out;
97 }
98
99 while (st->run) {
100 st->wh(st->sampv, st->sampc, st->arg);
101 sio_write(st->hdl, st->sampv, st->sampc*2);
102 }
103
104 out:
105 return NULL;
106 }
107
108
ausrc_destructor(void * arg)109 static void ausrc_destructor(void *arg)
110 {
111 struct ausrc_st *st = arg;
112
113 if (st->run) {
114 st->run = false;
115 (void)pthread_join(st->thread, NULL);
116 }
117
118 if (st->hdl)
119 sio_close(st->hdl);
120
121 mem_deref(st->sampv);
122 }
123
124
auplay_destructor(void * arg)125 static void auplay_destructor(void *arg)
126 {
127 struct auplay_st *st = arg;
128
129 if (st->run) {
130 st->run = false;
131 (void)pthread_join(st->thread, NULL);
132 }
133
134 if (st->hdl)
135 sio_close(st->hdl);
136
137 mem_deref(st->sampv);
138 }
139
140
src_alloc(struct ausrc_st ** stp,const struct ausrc * as,struct media_ctx ** ctx,struct ausrc_prm * prm,const char * device,ausrc_read_h * rh,ausrc_error_h * errh,void * arg)141 static int src_alloc(struct ausrc_st **stp, const struct ausrc *as,
142 struct media_ctx **ctx,
143 struct ausrc_prm *prm, const char *device,
144 ausrc_read_h *rh, ausrc_error_h *errh, void *arg)
145 {
146 struct ausrc_st *st;
147 struct sio_par *par = NULL;
148 int err;
149 const char *name;
150
151 (void)ctx;
152 (void)errh;
153
154 if (!stp || !as || !prm)
155 return EINVAL;
156
157 if (prm->fmt != AUFMT_S16LE) {
158 warning("sndio: source: unsupported sample format (%s)\n",
159 aufmt_name(prm->fmt));
160 return ENOTSUP;
161 }
162
163 name = (str_isset(device)) ? device : SIO_DEVANY;
164
165 if ((st = mem_zalloc(sizeof(*st), ausrc_destructor)) == NULL)
166 return ENOMEM;
167
168 st->as = as;
169 st->rh = rh;
170 st->arg = arg;
171 st->hdl = sio_open(name, SIO_REC, 0);
172
173 if (!st->hdl) {
174 warning("sndio: could not open ausrc device '%s'\n", name);
175 err = EINVAL;
176 goto out;
177 }
178
179 par = sndio_initpar(prm->srate, prm->ch);
180 if (!par) {
181 err = ENOMEM;
182 goto out;
183 }
184
185 if (!sio_setpar(st->hdl, par)) {
186 err = EINVAL;
187 goto out;
188 }
189
190 if (!sio_getpar(st->hdl, par)) {
191 err = EINVAL;
192 goto out;
193 }
194
195 st->sampc = par->bufsz / 2;
196
197 st->sampv = mem_alloc(2 * st->sampc, NULL);
198 if (!st->sampv) {
199 err = ENOMEM;
200 goto out;
201 }
202
203 st->run = true;
204 err = pthread_create(&st->thread, NULL, read_thread, st);
205 if (err)
206 st->run = false;
207
208 out:
209 mem_deref(par);
210 if (err)
211 mem_deref(st);
212 else
213 *stp = st;
214
215 return err;
216 }
217
218
play_alloc(struct auplay_st ** stp,const struct auplay * ap,struct auplay_prm * prm,const char * device,auplay_write_h * wh,void * arg)219 static int play_alloc(struct auplay_st **stp, const struct auplay *ap,
220 struct auplay_prm *prm, const char *device,
221 auplay_write_h *wh, void *arg)
222 {
223 struct auplay_st *st;
224 struct sio_par *par = NULL;
225 int err;
226 const char *name;
227
228 if (!stp || !ap || !prm)
229 return EINVAL;
230
231 if (prm->fmt != AUFMT_S16LE) {
232 warning("sndio: playback: unsupported sample format (%s)\n",
233 aufmt_name(prm->fmt));
234 return ENOTSUP;
235 }
236
237 name = (str_isset(device)) ? device : SIO_DEVANY;
238
239 if ((st = mem_zalloc(sizeof(*st), auplay_destructor)) == NULL)
240 return ENOMEM;
241
242 st->ap = ap;
243 st->wh = wh;
244 st->arg = arg;
245 st->hdl = sio_open(name, SIO_PLAY, 0);
246
247 if (!st->hdl) {
248 warning("sndio: could not open auplay device '%s'\n", name);
249 err = EINVAL;
250 goto out;
251 }
252
253 par = sndio_initpar(prm->srate, prm->ch);
254 if (!par) {
255 err = ENOMEM;
256 goto out;
257 }
258
259 if (!sio_setpar(st->hdl, par)) {
260 err = EINVAL;
261 goto out;
262 }
263
264 if (!sio_getpar(st->hdl, par)) {
265 err = EINVAL;
266 goto out;
267 }
268
269 st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
270
271 st->sampv = mem_alloc(2 * st->sampc, NULL);
272 if (!st->sampv) {
273 err = ENOMEM;
274 goto out;
275 }
276
277 st->run = true;
278 err = pthread_create(&st->thread, NULL, write_thread, st);
279 if (err)
280 st->run = false;
281
282 out:
283 mem_deref(par);
284 if (err)
285 mem_deref(st);
286 else
287 *stp = st;
288
289 return err;
290 }
291
292
sndio_init(void)293 static int sndio_init(void)
294 {
295 int err = 0;
296
297 err |= ausrc_register(&ausrc, baresip_ausrcl(), "sndio", src_alloc);
298 err |= auplay_register(&auplay, baresip_auplayl(),
299 "sndio", play_alloc);
300
301 return err;
302 }
303
304
sndio_close(void)305 static int sndio_close(void)
306 {
307 ausrc = mem_deref(ausrc);
308 auplay = mem_deref(auplay);
309
310 return 0;
311 }
312
313
314 EXPORT_SYM const struct mod_export DECL_EXPORTS(sndio) = {
315 "sndio",
316 "sound",
317 sndio_init,
318 sndio_close
319 };
320