1 /* RetroArch - A frontend for libretro.
2 * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3 * Copyright (C) 2012-2015 - Michael Lelli
4 *
5 * RetroArch is free software: you can redistribute it and/or modify it under the terms
6 * of the GNU General Public License as published by the Free Software Found-
7 * ation, either version 3 of the License, or (at your option) any later version.
8 *
9 * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 * PURPOSE. See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along with RetroArch.
14 * If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include <stdlib.h>
18
19 #include <lists/string_list.h>
20
21 #include <alsa/asoundlib.h>
22
23 #include <rthreads/rthreads.h>
24 #include <queues/fifo_queue.h>
25 #include <string/stdstring.h>
26
27 #include "../../retroarch.h"
28 #include "../../verbosity.h"
29
30 #define TRY_ALSA(x) if (x < 0) \
31 goto error;
32
33 typedef struct alsa_thread
34 {
35 snd_pcm_t *pcm;
36 fifo_buffer_t *buffer;
37 sthread_t *worker_thread;
38 slock_t *fifo_lock;
39 scond_t *cond;
40 slock_t *cond_lock;
41 size_t buffer_size;
42 size_t period_size;
43 snd_pcm_uframes_t period_frames;
44 bool nonblock;
45 bool is_paused;
46 bool has_float;
47 volatile bool thread_dead;
48 } alsa_thread_t;
49
alsa_worker_thread(void * data)50 static void alsa_worker_thread(void *data)
51 {
52 alsa_thread_t *alsa = (alsa_thread_t*)data;
53 uint8_t *buf = (uint8_t *)calloc(1, alsa->period_size);
54
55 if (!buf)
56 {
57 RARCH_ERR("failed to allocate audio buffer");
58 goto end;
59 }
60
61 while (!alsa->thread_dead)
62 {
63 size_t avail;
64 size_t fifo_size;
65 snd_pcm_sframes_t frames;
66 slock_lock(alsa->fifo_lock);
67 avail = FIFO_READ_AVAIL(alsa->buffer);
68 fifo_size = MIN(alsa->period_size, avail);
69 fifo_read(alsa->buffer, buf, fifo_size);
70 scond_signal(alsa->cond);
71 slock_unlock(alsa->fifo_lock);
72
73 /* If underrun, fill rest with silence. */
74 memset(buf + fifo_size, 0, alsa->period_size - fifo_size);
75
76 frames = snd_pcm_writei(alsa->pcm, buf, alsa->period_frames);
77
78 if (frames == -EPIPE || frames == -EINTR ||
79 frames == -ESTRPIPE)
80 {
81 if (snd_pcm_recover(alsa->pcm, frames, 1) < 0)
82 {
83 RARCH_ERR("[ALSA]: (#2) Failed to recover from error (%s)\n",
84 snd_strerror(frames));
85 break;
86 }
87
88 continue;
89 }
90 else if (frames < 0)
91 {
92 RARCH_ERR("[ALSA]: Unknown error occurred (%s).\n",
93 snd_strerror(frames));
94 break;
95 }
96 }
97
98 end:
99 slock_lock(alsa->cond_lock);
100 alsa->thread_dead = true;
101 scond_signal(alsa->cond);
102 slock_unlock(alsa->cond_lock);
103 free(buf);
104 }
105
alsa_thread_use_float(void * data)106 static bool alsa_thread_use_float(void *data)
107 {
108 alsa_thread_t *alsa = (alsa_thread_t*)data;
109 return alsa->has_float;
110 }
111
alsathread_find_float_format(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)112 static bool alsathread_find_float_format(snd_pcm_t *pcm,
113 snd_pcm_hw_params_t *params)
114 {
115 if (snd_pcm_hw_params_test_format(pcm, params, SND_PCM_FORMAT_FLOAT) == 0)
116 {
117 RARCH_LOG("ALSA: Using floating point format.\n");
118 return true;
119 }
120 RARCH_LOG("ALSA: Using signed 16-bit format.\n");
121 return false;
122 }
123
alsa_thread_free(void * data)124 static void alsa_thread_free(void *data)
125 {
126 alsa_thread_t *alsa = (alsa_thread_t*)data;
127
128 if (alsa)
129 {
130 if (alsa->worker_thread)
131 {
132 slock_lock(alsa->cond_lock);
133 alsa->thread_dead = true;
134 slock_unlock(alsa->cond_lock);
135 sthread_join(alsa->worker_thread);
136 }
137 if (alsa->buffer)
138 fifo_free(alsa->buffer);
139 if (alsa->cond)
140 scond_free(alsa->cond);
141 if (alsa->fifo_lock)
142 slock_free(alsa->fifo_lock);
143 if (alsa->cond_lock)
144 slock_free(alsa->cond_lock);
145 if (alsa->pcm)
146 {
147 snd_pcm_drop(alsa->pcm);
148 snd_pcm_close(alsa->pcm);
149 }
150 free(alsa);
151 }
152 }
153
alsa_thread_init(const char * device,unsigned rate,unsigned latency,unsigned block_frames,unsigned * new_rate)154 static void *alsa_thread_init(const char *device,
155 unsigned rate, unsigned latency,
156 unsigned block_frames,
157 unsigned *new_rate)
158 {
159 snd_pcm_uframes_t buffer_size;
160 snd_pcm_format_t format;
161 snd_pcm_hw_params_t *params = NULL;
162 snd_pcm_sw_params_t *sw_params = NULL;
163 const char *alsa_dev = device ? device : "default";
164 unsigned latency_usec = latency * 1000 / 2;
165 unsigned channels = 2;
166 unsigned periods = 4;
167 alsa_thread_t *alsa = (alsa_thread_t*)
168 calloc(1, sizeof(alsa_thread_t));
169
170 if (!alsa)
171 return NULL;
172
173 TRY_ALSA(snd_pcm_open(&alsa->pcm, alsa_dev, SND_PCM_STREAM_PLAYBACK, 0));
174
175 TRY_ALSA(snd_pcm_hw_params_malloc(¶ms));
176 TRY_ALSA(snd_pcm_hw_params_any(alsa->pcm, params));
177
178 alsa->has_float = alsathread_find_float_format(alsa->pcm, params);
179 format = alsa->has_float ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16;
180
181 TRY_ALSA(snd_pcm_hw_params_set_access(
182 alsa->pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED));
183 TRY_ALSA(snd_pcm_hw_params_set_format(alsa->pcm, params, format));
184 TRY_ALSA(snd_pcm_hw_params_set_channels(alsa->pcm, params, channels));
185 TRY_ALSA(snd_pcm_hw_params_set_rate(alsa->pcm, params, rate, 0));
186
187 TRY_ALSA(snd_pcm_hw_params_set_buffer_time_near(
188 alsa->pcm, params, &latency_usec, NULL));
189 TRY_ALSA(snd_pcm_hw_params_set_periods_near(
190 alsa->pcm, params, &periods, NULL));
191
192 TRY_ALSA(snd_pcm_hw_params(alsa->pcm, params));
193
194 /* Shouldn't have to bother with this,
195 * but some drivers are apparently broken. */
196 if (snd_pcm_hw_params_get_period_size(params, &alsa->period_frames, NULL))
197 snd_pcm_hw_params_get_period_size_min(
198 params, &alsa->period_frames, NULL);
199 RARCH_LOG("ALSA: Period size: %d frames\n", (int)alsa->period_frames);
200 if (snd_pcm_hw_params_get_buffer_size(params, &buffer_size))
201 snd_pcm_hw_params_get_buffer_size_max(params, &buffer_size);
202 RARCH_LOG("ALSA: Buffer size: %d frames\n", (int)buffer_size);
203
204 alsa->buffer_size = snd_pcm_frames_to_bytes(alsa->pcm, buffer_size);
205 alsa->period_size = snd_pcm_frames_to_bytes(alsa->pcm, alsa->period_frames);
206
207 TRY_ALSA(snd_pcm_sw_params_malloc(&sw_params));
208 TRY_ALSA(snd_pcm_sw_params_current(alsa->pcm, sw_params));
209 TRY_ALSA(snd_pcm_sw_params_set_start_threshold(
210 alsa->pcm, sw_params, buffer_size / 2));
211 TRY_ALSA(snd_pcm_sw_params(alsa->pcm, sw_params));
212
213 snd_pcm_hw_params_free(params);
214 snd_pcm_sw_params_free(sw_params);
215
216 alsa->fifo_lock = slock_new();
217 alsa->cond_lock = slock_new();
218 alsa->cond = scond_new();
219 alsa->buffer = fifo_new(alsa->buffer_size);
220 if (!alsa->fifo_lock || !alsa->cond_lock || !alsa->cond || !alsa->buffer)
221 goto error;
222
223 alsa->worker_thread = sthread_create(alsa_worker_thread, alsa);
224 if (!alsa->worker_thread)
225 {
226 RARCH_ERR("error initializing worker thread");
227 goto error;
228 }
229
230 return alsa;
231
232 error:
233 RARCH_ERR("ALSA: Failed to initialize...\n");
234 if (params)
235 snd_pcm_hw_params_free(params);
236
237 if (sw_params)
238 snd_pcm_sw_params_free(sw_params);
239
240 alsa_thread_free(alsa);
241
242 return NULL;
243 }
244
alsa_thread_write(void * data,const void * buf,size_t size)245 static ssize_t alsa_thread_write(void *data, const void *buf, size_t size)
246 {
247 alsa_thread_t *alsa = (alsa_thread_t*)data;
248
249 if (alsa->thread_dead)
250 return -1;
251
252 if (alsa->nonblock)
253 {
254 size_t avail;
255 size_t write_amt;
256
257 slock_lock(alsa->fifo_lock);
258 avail = FIFO_WRITE_AVAIL(alsa->buffer);
259 write_amt = MIN(avail, size);
260
261 fifo_write(alsa->buffer, buf, write_amt);
262 slock_unlock(alsa->fifo_lock);
263
264 return write_amt;
265 }
266 else
267 {
268 size_t written = 0;
269 while (written < size && !alsa->thread_dead)
270 {
271 size_t avail;
272 slock_lock(alsa->fifo_lock);
273 avail = FIFO_WRITE_AVAIL(alsa->buffer);
274
275 if (avail == 0)
276 {
277 slock_unlock(alsa->fifo_lock);
278 slock_lock(alsa->cond_lock);
279 if (!alsa->thread_dead)
280 scond_wait(alsa->cond, alsa->cond_lock);
281 slock_unlock(alsa->cond_lock);
282 }
283 else
284 {
285 size_t write_amt = MIN(size - written, avail);
286 fifo_write(alsa->buffer,
287 (const char*)buf + written, write_amt);
288 slock_unlock(alsa->fifo_lock);
289 written += write_amt;
290 }
291 }
292 return written;
293 }
294 }
295
alsa_thread_alive(void * data)296 static bool alsa_thread_alive(void *data)
297 {
298 alsa_thread_t *alsa = (alsa_thread_t*)data;
299 if (!alsa)
300 return false;
301 return !alsa->is_paused;
302 }
303
alsa_thread_stop(void * data)304 static bool alsa_thread_stop(void *data)
305 {
306 alsa_thread_t *alsa = (alsa_thread_t*)data;
307
308 if (alsa)
309 alsa->is_paused = true;
310 return true;
311 }
312
alsa_thread_set_nonblock_state(void * data,bool state)313 static void alsa_thread_set_nonblock_state(void *data, bool state)
314 {
315 alsa_thread_t *alsa = (alsa_thread_t*)data;
316 alsa->nonblock = state;
317 }
318
alsa_thread_start(void * data,bool is_shutdown)319 static bool alsa_thread_start(void *data, bool is_shutdown)
320 {
321 alsa_thread_t *alsa = (alsa_thread_t*)data;
322
323 if (alsa)
324 alsa->is_paused = false;
325 return true;
326 }
327
alsa_thread_write_avail(void * data)328 static size_t alsa_thread_write_avail(void *data)
329 {
330 alsa_thread_t *alsa = (alsa_thread_t*)data;
331 size_t val;
332
333 if (alsa->thread_dead)
334 return 0;
335 slock_lock(alsa->fifo_lock);
336 val = FIFO_WRITE_AVAIL(alsa->buffer);
337 slock_unlock(alsa->fifo_lock);
338 return val;
339 }
340
alsa_thread_buffer_size(void * data)341 static size_t alsa_thread_buffer_size(void *data)
342 {
343 alsa_thread_t *alsa = (alsa_thread_t*)data;
344 return alsa->buffer_size;
345 }
346
alsa_thread_device_list_new(void * data)347 static void *alsa_thread_device_list_new(void *data)
348 {
349 void **hints, **n;
350 union string_list_elem_attr attr;
351 struct string_list *s = string_list_new();
352
353 if (!s)
354 return NULL;
355
356 attr.i = 0;
357
358 if (snd_device_name_hint(-1, "pcm", &hints) != 0)
359 goto error;
360
361 n = hints;
362
363 while (*n)
364 {
365 char *name = snd_device_name_get_hint(*n, "NAME");
366 char *io = snd_device_name_get_hint(*n, "IOID");
367 char *desc = snd_device_name_get_hint(*n, "DESC");
368
369 /* description of device IOID - input / output identifcation
370 * ("Input" or "Output"), NULL means both) */
371
372 if (!io || string_is_equal(io,"Output"))
373 string_list_append(s, name, attr);
374
375 if (name)
376 free(name);
377 if (io)
378 free(io);
379 if (desc)
380 free(desc);
381
382 n++;
383 }
384
385 /* free hint buffer too */
386 snd_device_name_free_hint(hints);
387
388 return s;
389
390 error:
391 string_list_free(s);
392 return NULL;
393 }
394
alsa_thread_device_list_free(void * data,void * array_list_data)395 static void alsa_thread_device_list_free(void *data, void *array_list_data)
396 {
397 struct string_list *s = (struct string_list*)array_list_data;
398
399 if (!s)
400 return;
401
402 string_list_free(s);
403 }
404
405 audio_driver_t audio_alsathread = {
406 alsa_thread_init,
407 alsa_thread_write,
408 alsa_thread_stop,
409 alsa_thread_start,
410 alsa_thread_alive,
411 alsa_thread_set_nonblock_state,
412 alsa_thread_free,
413 alsa_thread_use_float,
414 "alsathread",
415 alsa_thread_device_list_new,
416 alsa_thread_device_list_free,
417 alsa_thread_write_avail,
418 alsa_thread_buffer_size,
419 };
420