1 /*
2  * Copyright 2008-2013 Various Authors
3  * Copyright 2008 Jonathan Kleinehellefort
4  * Copyright 2004-2005 Timo Hirvonen
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 /*
21  * snd_pcm_state_t:
22  *
23  * Open
24  * SND_PCM_STATE_OPEN = 0,
25  *
26  * Setup installed
27  * SND_PCM_STATE_SETUP = 1,
28  *
29  * Ready to start
30  * SND_PCM_STATE_PREPARED = 2,
31  *
32  * Running
33  * SND_PCM_STATE_RUNNING = 3,
34  *
35  * Stopped: underrun (playback) or overrun (capture) detected
36  * SND_PCM_STATE_XRUN = 4,
37  *
38  * Draining: running (playback) or stopped (capture)
39  * SND_PCM_STATE_DRAINING = 5,
40  *
41  * Paused
42  * SND_PCM_STATE_PAUSED = 6,
43  *
44  * Hardware is suspended
45  * SND_PCM_STATE_SUSPENDED = 7,
46  *
47  * Hardware is disconnected
48  * SND_PCM_STATE_DISCONNECTED = 8,
49  */
50 
51 #include "../op.h"
52 #include "../utils.h"
53 #include "../xmalloc.h"
54 #include "../sf.h"
55 #include "../debug.h"
56 
57 #define ALSA_PCM_NEW_HW_PARAMS_API
58 #define ALSA_PCM_NEW_SW_PARAMS_API
59 
60 #include <alsa/asoundlib.h>
61 
62 static sample_format_t alsa_sf;
63 static snd_pcm_t *alsa_handle;
64 static snd_pcm_format_t alsa_fmt;
65 static int alsa_can_pause;
66 static snd_pcm_status_t *status;
67 
68 /* bytes (bits * channels / 8) */
69 static int alsa_frame_size;
70 
71 /* configuration */
72 static char *alsa_dsp_device = NULL;
73 
74 #if 0
75 #define debug_ret(func, ret) \
76 	d_print("%s returned %d %s\n", func, ret, ret < 0 ? snd_strerror(ret) : "")
77 #else
78 #define debug_ret(func, ret) do { } while (0)
79 #endif
80 
alsa_error_to_op_error(int err)81 static int alsa_error_to_op_error(int err)
82 {
83 	if (!err)
84 		return OP_ERROR_SUCCESS;
85 	err = -err;
86 	if (err < SND_ERROR_BEGIN) {
87 		errno = err;
88 		return -OP_ERROR_ERRNO;
89 	}
90 	return -OP_ERROR_INTERNAL;
91 }
92 
93 /* we don't want error messages to stderr */
error_handler(const char * file,int line,const char * function,int err,const char * fmt,...)94 static void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
95 {
96 }
97 
op_alsa_init(void)98 static int op_alsa_init(void)
99 {
100 	int rc;
101 
102 	snd_lib_error_set_handler(error_handler);
103 
104 	if (alsa_dsp_device == NULL)
105 		alsa_dsp_device = xstrdup("default");
106 	rc = snd_pcm_status_malloc(&status);
107 	if (rc < 0) {
108 		free(alsa_dsp_device);
109 		alsa_dsp_device = NULL;
110 		errno = ENOMEM;
111 		return -OP_ERROR_ERRNO;
112 	}
113 	return OP_ERROR_SUCCESS;
114 }
115 
op_alsa_exit(void)116 static int op_alsa_exit(void)
117 {
118 	snd_pcm_status_free(status);
119 	free(alsa_dsp_device);
120 	alsa_dsp_device = NULL;
121 	return OP_ERROR_SUCCESS;
122 }
123 
124 /* randomize hw params */
alsa_set_hw_params(void)125 static int alsa_set_hw_params(void)
126 {
127 	snd_pcm_hw_params_t *hwparams = NULL;
128 	unsigned int buffer_time_max = 300 * 1000; /* us */
129 	const char *cmd;
130 	unsigned int rate;
131 	int rc, dir;
132 
133 	snd_pcm_hw_params_malloc(&hwparams);
134 
135 	cmd = "snd_pcm_hw_params_any";
136 	rc = snd_pcm_hw_params_any(alsa_handle, hwparams);
137 	if (rc < 0)
138 		goto error;
139 
140 	cmd = "snd_pcm_hw_params_set_buffer_time_max";
141 	rc = snd_pcm_hw_params_set_buffer_time_max(alsa_handle, hwparams,
142 	                                           &buffer_time_max, &dir);
143 	if (rc < 0)
144 		goto error;
145 
146 	alsa_can_pause = snd_pcm_hw_params_can_pause(hwparams);
147 	d_print("can pause = %d\n", alsa_can_pause);
148 
149 	cmd = "snd_pcm_hw_params_set_access";
150 	rc = snd_pcm_hw_params_set_access(alsa_handle, hwparams,
151 			SND_PCM_ACCESS_RW_INTERLEAVED);
152 	if (rc < 0)
153 		goto error;
154 
155 	alsa_fmt = snd_pcm_build_linear_format(sf_get_bits(alsa_sf), sf_get_bits(alsa_sf),
156 			sf_get_signed(alsa_sf) ? 0 : 1,
157 			sf_get_bigendian(alsa_sf));
158 	cmd = "snd_pcm_hw_params_set_format";
159 	rc = snd_pcm_hw_params_set_format(alsa_handle, hwparams, alsa_fmt);
160 	if (rc < 0)
161 		goto error;
162 
163 	cmd = "snd_pcm_hw_params_set_channels";
164 	rc = snd_pcm_hw_params_set_channels(alsa_handle, hwparams, sf_get_channels(alsa_sf));
165 	if (rc < 0)
166 		goto error;
167 
168 	cmd = "snd_pcm_hw_params_set_rate";
169 	rate = sf_get_rate(alsa_sf);
170 	dir = 0;
171 	rc = snd_pcm_hw_params_set_rate_near(alsa_handle, hwparams, &rate, &dir);
172 	if (rc < 0)
173 		goto error;
174 	d_print("rate=%d\n", rate);
175 
176 	cmd = "snd_pcm_hw_params";
177 	rc = snd_pcm_hw_params(alsa_handle, hwparams);
178 	if (rc < 0)
179 		goto error;
180 	goto out;
181 error:
182 	d_print("%s: error: %s\n", cmd, snd_strerror(rc));
183 out:
184 	snd_pcm_hw_params_free(hwparams);
185 	return rc;
186 }
187 
op_alsa_open(sample_format_t sf,const channel_position_t * channel_map)188 static int op_alsa_open(sample_format_t sf, const channel_position_t *channel_map)
189 {
190 	int rc;
191 
192 	alsa_sf = sf;
193 	alsa_frame_size = sf_get_frame_size(alsa_sf);
194 
195 	rc = snd_pcm_open(&alsa_handle, alsa_dsp_device, SND_PCM_STREAM_PLAYBACK, 0);
196 	if (rc < 0)
197 		goto error;
198 
199 	rc = alsa_set_hw_params();
200 	if (rc)
201 		goto close_error;
202 
203 	rc = snd_pcm_prepare(alsa_handle);
204 	if (rc < 0)
205 		goto close_error;
206 	return OP_ERROR_SUCCESS;
207 close_error:
208 	snd_pcm_close(alsa_handle);
209 error:
210 	return alsa_error_to_op_error(rc);
211 }
212 
213 static int op_alsa_write(const char *buffer, int count);
214 
op_alsa_close(void)215 static int op_alsa_close(void)
216 {
217 	int rc;
218 
219 	rc = snd_pcm_drain(alsa_handle);
220 	debug_ret("snd_pcm_drain", rc);
221 
222 	rc = snd_pcm_close(alsa_handle);
223 	debug_ret("snd_pcm_close", rc);
224 	return alsa_error_to_op_error(rc);
225 }
226 
op_alsa_drop(void)227 static int op_alsa_drop(void)
228 {
229 	int rc;
230 
231 	rc = snd_pcm_drop(alsa_handle);
232 	debug_ret("snd_pcm_drop", rc);
233 
234 	rc = snd_pcm_prepare(alsa_handle);
235 	debug_ret("snd_pcm_prepare", rc);
236 
237 	/* drop set state to SETUP
238 	 * prepare set state to PREPARED
239 	 *
240 	 * so if old state was PAUSED we can't UNPAUSE (see op_alsa_unpause)
241 	 */
242 	return alsa_error_to_op_error(rc);
243 }
244 
op_alsa_write(const char * buffer,int count)245 static int op_alsa_write(const char *buffer, int count)
246 {
247 	int rc, len;
248 	int recovered = 0;
249 
250 	len = count / alsa_frame_size;
251 again:
252 	rc = snd_pcm_writei(alsa_handle, buffer, len);
253 	if (rc < 0) {
254 		// rc _should_ be either -EBADFD, -EPIPE or -ESTRPIPE
255 		if (!recovered && (rc == -EINTR || rc == -EPIPE || rc == -ESTRPIPE)) {
256 			d_print("snd_pcm_writei failed: %s, trying to recover\n",
257 					snd_strerror(rc));
258 			recovered++;
259 			// this handles -EINTR, -EPIPE and -ESTRPIPE
260 			// for other errors it just returns the error code
261 			rc = snd_pcm_recover(alsa_handle, rc, 1);
262 			if (!rc)
263 				goto again;
264 		}
265 
266 		/* this handles EAGAIN too which is not critical error */
267 		return alsa_error_to_op_error(rc);
268 	}
269 
270 	rc *= alsa_frame_size;
271 	return rc;
272 }
273 
op_alsa_buffer_space(void)274 static int op_alsa_buffer_space(void)
275 {
276 	int rc;
277 	snd_pcm_sframes_t f;
278 
279 	f = snd_pcm_avail_update(alsa_handle);
280 	while (f < 0) {
281 		d_print("snd_pcm_avail_update failed: %s, trying to recover\n",
282 			snd_strerror(f));
283 		rc = snd_pcm_recover(alsa_handle, f, 1);
284 		if (rc < 0) {
285 			d_print("recovery failed: %s\n", snd_strerror(rc));
286 			return alsa_error_to_op_error(rc);
287 		}
288 		f = snd_pcm_avail_update(alsa_handle);
289 	}
290 
291 	return f * alsa_frame_size;
292 }
293 
op_alsa_pause(void)294 static int op_alsa_pause(void)
295 {
296 	int rc = 0;
297 	if (alsa_can_pause) {
298 		snd_pcm_state_t state = snd_pcm_state(alsa_handle);
299 		if (state == SND_PCM_STATE_PREPARED) {
300 			// state is PREPARED -> no need to pause
301 		} else if (state == SND_PCM_STATE_RUNNING) {
302 			// state is RUNNING - > pause
303 
304 			// infinite timeout
305 			rc = snd_pcm_wait(alsa_handle, -1);
306 			debug_ret("snd_pcm_wait", rc);
307 
308 			rc = snd_pcm_pause(alsa_handle, 1);
309 			debug_ret("snd_pcm_pause", rc);
310 		} else {
311 			d_print("error: state is not RUNNING or PREPARED\n");
312 			rc = -OP_ERROR_INTERNAL;
313 		}
314 	} else {
315 		rc = snd_pcm_drop(alsa_handle);
316 		debug_ret("snd_pcm_drop", rc);
317 	}
318 	return alsa_error_to_op_error(rc);
319 }
320 
op_alsa_unpause(void)321 static int op_alsa_unpause(void)
322 {
323 	int rc = 0;
324 	if (alsa_can_pause) {
325 		snd_pcm_state_t state = snd_pcm_state(alsa_handle);
326 		if (state == SND_PCM_STATE_PREPARED) {
327 			// state is PREPARED -> no need to unpause
328 		} else if (state == SND_PCM_STATE_PAUSED) {
329 			// state is PAUSED -> unpause
330 
331 			// infinite timeout
332 			rc = snd_pcm_wait(alsa_handle, -1);
333 			debug_ret("snd_pcm_wait", rc);
334 
335 			rc = snd_pcm_pause(alsa_handle, 0);
336 			debug_ret("snd_pcm_pause", rc);
337 		} else {
338 			d_print("error: state is not PAUSED nor PREPARED\n");
339 			rc = -OP_ERROR_INTERNAL;
340 		}
341 	} else {
342 		rc = snd_pcm_prepare(alsa_handle);
343 		debug_ret("snd_pcm_prepare", rc);
344 	}
345 	return alsa_error_to_op_error(rc);
346 }
347 
op_alsa_set_device(const char * val)348 static int op_alsa_set_device(const char *val)
349 {
350 	free(alsa_dsp_device);
351 	alsa_dsp_device = xstrdup(val);
352 	return OP_ERROR_SUCCESS;
353 }
354 
op_alsa_get_device(char ** val)355 static int op_alsa_get_device(char **val)
356 {
357 	if (alsa_dsp_device)
358 		*val = xstrdup(alsa_dsp_device);
359 	return OP_ERROR_SUCCESS;
360 }
361 
362 const struct output_plugin_ops op_pcm_ops = {
363 	.init = op_alsa_init,
364 	.exit = op_alsa_exit,
365 	.open = op_alsa_open,
366 	.close = op_alsa_close,
367 	.drop = op_alsa_drop,
368 	.write = op_alsa_write,
369 	.buffer_space = op_alsa_buffer_space,
370 	.pause = op_alsa_pause,
371 	.unpause = op_alsa_unpause,
372 };
373 
374 const struct output_plugin_opt op_pcm_options[] = {
375 	OPT(op_alsa, device),
376 	{ NULL },
377 };
378 
379 const int op_priority = 0;
380 const unsigned op_abi_version = OP_ABI_VERSION;
381