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