1 /* ______ ___ ___
2 * /\ _ \ /\_ \ /\_ \
3 * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
5 * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
6 * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 * /\____/
9 * \_/__/
10 *
11 * PulseAudio sound driver.
12 *
13 * By Matthew Leverton.
14 *
15 * See readme.txt for copyright information.
16 */
17
18 #include "allegro5/allegro.h"
19 #include "allegro5/internal/aintern_audio.h"
20
21 #include <pulse/simple.h>
22 #include <pulse/error.h>
23 #include <pulse/introspect.h>
24 #include <pulse/mainloop.h>
25 #include <stdlib.h>
26
27 ALLEGRO_DEBUG_CHANNEL("PulseAudio")
28
29 enum PULSEAUDIO_VOICE_STATUS {
30 PV_IDLE,
31 PV_PLAYING,
32 PV_STOPPING,
33 PV_JOIN
34 };
35
36 typedef struct PULSEAUDIO_VOICE
37 {
38 pa_simple *s;
39 unsigned int buffer_size_in_frames;
40 unsigned int frame_size_in_bytes;
41
42 ALLEGRO_THREAD *poll_thread;
43 /* status_cond and status are protected by voice->mutex.
44 * Using another mutex introduces a deadlock if waiting for a change in
45 * status (while holding voice->mutex, acquired by a higher layer)
46 * and the background thread tries to acquire voice->mutex as well.
47 */
48 ALLEGRO_COND *status_cond;
49 enum PULSEAUDIO_VOICE_STATUS status;
50
51 // direct buffer (non-streaming):
52 ALLEGRO_MUTEX *buffer_mutex;
53 char *buffer;
54 char *buffer_end;
55 } PULSEAUDIO_VOICE;
56
57 #define DEFAULT_BUFFER_SIZE 1024
58 #define MIN_BUFFER_SIZE 128
59
get_buffer_size(const ALLEGRO_CONFIG * config)60 static unsigned int get_buffer_size(const ALLEGRO_CONFIG *config)
61 {
62 if (config) {
63 const char *val = al_get_config_value(config,
64 "pulseaudio", "buffer_size");
65 if (val && val[0] != '\0') {
66 int n = atoi(val);
67 if (n < MIN_BUFFER_SIZE)
68 n = MIN_BUFFER_SIZE;
69 return n;
70 }
71 }
72
73 return DEFAULT_BUFFER_SIZE;
74 }
75
sink_info_cb(pa_context * c,const pa_sink_info * i,int eol,void * userdata)76 static void sink_info_cb(pa_context *c, const pa_sink_info *i, int eol,
77 void *userdata)
78 {
79 (void)c;
80 (void)eol;
81
82 pa_sink_state_t *ret = userdata;
83 if (!i)
84 return;
85 *ret = i->state;
86 }
87
pulseaudio_open(void)88 static int pulseaudio_open(void)
89 {
90 /* Use PA_CONTEXT_NOAUTOSPAWN to see if a PA server is running.
91 * If not, fail - we're better off using ALSA/OSS.
92 *
93 * Also check for suspended PA - again better using ALSA/OSS in
94 * that case (pa_simple_write just blocks until PA is unsuspended
95 * otherwise).
96 *
97 * TODO: Maybe we should have a force flag to the audio driver
98 * open method, which in the case of PA would spawn a server if
99 * none is running (and also unsuspend?).
100 */
101
102 pa_mainloop *mainloop = pa_mainloop_new();
103 pa_context *c = pa_context_new(pa_mainloop_get_api(mainloop),
104 al_get_app_name());
105 if (!c) {
106 pa_mainloop_free(mainloop);
107 return 1;
108 }
109
110 pa_context_connect(c, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
111
112 while (1) {
113 /* Don't block or it will hang if there is no server to connect to. */
114 const int blocking = 0;
115 if (pa_mainloop_iterate(mainloop, blocking, NULL) < 0) {
116 ALLEGRO_ERROR("pa_mainloop_iterate failed\n");
117 pa_context_disconnect(c);
118 pa_context_unref(c);
119 pa_mainloop_free(mainloop);
120 return 1;
121 }
122 pa_context_state_t s = pa_context_get_state(c);
123 if (s == PA_CONTEXT_READY) {
124 ALLEGRO_DEBUG("PA_CONTEXT_READY\n");
125 break;
126 }
127 if (s == PA_CONTEXT_FAILED) {
128 ALLEGRO_ERROR("PA_CONTEXT_FAILED\n");
129 pa_context_disconnect(c);
130 pa_context_unref(c);
131 pa_mainloop_free(mainloop);
132 return 1;
133 }
134 }
135
136 pa_sink_state_t state = 0;
137 pa_operation *op = pa_context_get_sink_info_list(c, sink_info_cb, &state);
138 while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) {
139 pa_mainloop_iterate(mainloop, 1, NULL);
140 }
141 /*if (state == PA_SINK_SUSPENDED) {
142 pa_context_disconnect(c);
143 pa_context_unref(c);
144 pa_mainloop_free(mainloop);
145 return 1;
146 }*/
147 pa_operation_unref(op);
148 pa_context_disconnect(c);
149 pa_context_unref(c);
150 pa_mainloop_free(mainloop);
151 return 0;
152 }
153
pulseaudio_close(void)154 static void pulseaudio_close(void)
155 {
156 }
157
pulseaudio_update(ALLEGRO_THREAD * self,void * data)158 static void *pulseaudio_update(ALLEGRO_THREAD *self, void *data)
159 {
160 ALLEGRO_VOICE *voice = data;
161 PULSEAUDIO_VOICE *pv = voice->extra;
162 (void)self;
163
164 for (;;) {
165 enum PULSEAUDIO_VOICE_STATUS status;
166
167 al_lock_mutex(voice->mutex);
168 while ((status = pv->status) == PV_IDLE) {
169 al_wait_cond(pv->status_cond, voice->mutex);
170 }
171 al_unlock_mutex(voice->mutex);
172
173 if (status == PV_JOIN) {
174 break;
175 }
176
177 if (status == PV_PLAYING) {
178 unsigned int frames = pv->buffer_size_in_frames;
179 if (voice->is_streaming) {
180 // streaming audio
181 const void *data = _al_voice_update(voice, voice->mutex, &frames);
182 if (data) {
183 pa_simple_write(pv->s, data,
184 frames * pv->frame_size_in_bytes, NULL);
185 }
186 }
187 else {
188 // direct buffer audio
189 al_lock_mutex(pv->buffer_mutex);
190 const char *data = pv->buffer;
191 unsigned int len = frames * pv->frame_size_in_bytes;
192 pv->buffer += frames * pv->frame_size_in_bytes;
193 if (pv->buffer > pv->buffer_end) {
194 len = pv->buffer_end - data;
195 pv->buffer = voice->attached_stream->spl_data.buffer.ptr;
196 voice->attached_stream->pos = 0;
197 if (voice->attached_stream->loop == ALLEGRO_PLAYMODE_ONCE) {
198 al_lock_mutex(voice->mutex);
199 pv->status = PV_STOPPING;
200 al_broadcast_cond(pv->status_cond);
201 al_unlock_mutex(voice->mutex);
202 }
203 }
204 else {
205 voice->attached_stream->pos += frames;
206 }
207 al_unlock_mutex(pv->buffer_mutex);
208
209 pa_simple_write(pv->s, data, len, NULL);
210 }
211 }
212 else if (status == PV_STOPPING) {
213 pa_simple_drain(pv->s, NULL);
214 al_lock_mutex(voice->mutex);
215 pv->status = PV_IDLE;
216 al_broadcast_cond(pv->status_cond);
217 al_unlock_mutex(voice->mutex);
218 }
219 }
220
221 return NULL;
222 }
223
pulseaudio_allocate_voice(ALLEGRO_VOICE * voice)224 static int pulseaudio_allocate_voice(ALLEGRO_VOICE *voice)
225 {
226 PULSEAUDIO_VOICE *pv = al_malloc(sizeof(PULSEAUDIO_VOICE));
227 pa_sample_spec ss;
228 pa_buffer_attr ba;
229
230 ss.channels = al_get_channel_count(voice->chan_conf);
231 ss.rate = voice->frequency;
232
233 if (voice->depth == ALLEGRO_AUDIO_DEPTH_UINT8)
234 ss.format = PA_SAMPLE_U8;
235 else if (voice->depth == ALLEGRO_AUDIO_DEPTH_INT16)
236 ss.format = PA_SAMPLE_S16NE;
237 #if PA_API_VERSION > 11
238 else if (voice->depth == ALLEGRO_AUDIO_DEPTH_INT24)
239 ss.format = PA_SAMPLE_S24NE;
240 #endif
241 else if (voice->depth == ALLEGRO_AUDIO_DEPTH_FLOAT32)
242 ss.format = PA_SAMPLE_FLOAT32NE;
243 else {
244 ALLEGRO_ERROR("Unsupported PulseAudio sound format.\n");
245 al_free(pv);
246 return 1;
247 }
248
249 // These settings match what pulseaudio does by default, but with a slightly lower latency.
250 // The latency can also be controlled via PULSE_LATENCY_MSEC environment variable.
251 ba.maxlength = -1;
252 ba.tlength = pa_usec_to_bytes(50 * 1000, &ss); // 50 ms of latency by default.
253 ba.prebuf = -1;
254 ba.minreq = -1;
255 ba.fragsize = -1;
256
257 pv->s = pa_simple_new(
258 NULL, // Use the default server.
259 al_get_app_name(),
260 PA_STREAM_PLAYBACK,
261 NULL, // Use the default device.
262 "Allegro Voice",
263 &ss,
264 NULL, // Use default channel map
265 &ba,
266 NULL // Ignore error code.
267 );
268
269 if (!pv->s) {
270 al_free(pv);
271 return 1;
272 }
273
274 voice->extra = pv;
275
276 pv->buffer_size_in_frames = get_buffer_size(al_get_system_config());
277 pv->frame_size_in_bytes = ss.channels * al_get_audio_depth_size(voice->depth);
278
279 pv->status = PV_IDLE;
280 //pv->status_mutex = al_create_mutex();
281 pv->status_cond = al_create_cond();
282 pv->buffer_mutex = al_create_mutex();
283
284 pv->poll_thread = al_create_thread(pulseaudio_update, (void*)voice);
285 al_start_thread(pv->poll_thread);
286
287 return 0;
288 }
289
pulseaudio_deallocate_voice(ALLEGRO_VOICE * voice)290 static void pulseaudio_deallocate_voice(ALLEGRO_VOICE *voice)
291 {
292 PULSEAUDIO_VOICE *pv = voice->extra;
293
294 al_lock_mutex(voice->mutex);
295 pv->status = PV_JOIN;
296 al_broadcast_cond(pv->status_cond);
297 al_unlock_mutex(voice->mutex);
298
299 /* We do NOT hold the voice mutex here, so this does NOT result in a
300 * deadlock when the thread calls _al_voice_update.
301 */
302 al_join_thread(pv->poll_thread, NULL);
303 al_destroy_thread(pv->poll_thread);
304
305 al_destroy_cond(pv->status_cond);
306 al_destroy_mutex(pv->buffer_mutex);
307
308 pa_simple_free(pv->s);
309 al_free(pv);
310 }
311
pulseaudio_load_voice(ALLEGRO_VOICE * voice,const void * data)312 static int pulseaudio_load_voice(ALLEGRO_VOICE *voice, const void *data)
313 {
314 PULSEAUDIO_VOICE *pv = voice->extra;
315 (void)data;
316
317 if (voice->attached_stream->loop == ALLEGRO_PLAYMODE_BIDIR) {
318 ALLEGRO_INFO("Backwards playing not supported by the driver.\n");
319 return 1;
320 }
321
322 voice->attached_stream->pos = 0;
323
324 pv->buffer = voice->attached_stream->spl_data.buffer.ptr;
325 pv->buffer_end = pv->buffer +
326 (voice->attached_stream->spl_data.len) * pv->frame_size_in_bytes;
327
328 return 0;
329 }
330
pulseaudio_unload_voice(ALLEGRO_VOICE * voice)331 static void pulseaudio_unload_voice(ALLEGRO_VOICE *voice)
332 {
333 (void) voice;
334 }
335
pulseaudio_start_voice(ALLEGRO_VOICE * voice)336 static int pulseaudio_start_voice(ALLEGRO_VOICE *voice)
337 {
338 PULSEAUDIO_VOICE *pv = voice->extra;
339 int ret;
340
341 /* We hold the voice->mutex already. */
342
343 if (pv->status == PV_IDLE) {
344 pv->status = PV_PLAYING;
345 al_broadcast_cond(pv->status_cond);
346 ret = 0;
347 }
348 else {
349 ret = 1;
350 }
351
352 return ret;
353 }
354
pulseaudio_stop_voice(ALLEGRO_VOICE * voice)355 static int pulseaudio_stop_voice(ALLEGRO_VOICE *voice)
356 {
357 PULSEAUDIO_VOICE *pv = voice->extra;
358
359 /* We hold the voice->mutex already. */
360
361 if (pv->status == PV_PLAYING) {
362 pv->status = PV_STOPPING;
363 al_broadcast_cond(pv->status_cond);
364 }
365
366 while (pv->status != PV_IDLE) {
367 al_wait_cond(pv->status_cond, voice->mutex);
368 }
369
370 return 0;
371 }
372
pulseaudio_voice_is_playing(const ALLEGRO_VOICE * voice)373 static bool pulseaudio_voice_is_playing(const ALLEGRO_VOICE *voice)
374 {
375 PULSEAUDIO_VOICE *pv = voice->extra;
376 return (pv->status == PV_PLAYING);
377 }
378
pulseaudio_get_voice_position(const ALLEGRO_VOICE * voice)379 static unsigned int pulseaudio_get_voice_position(const ALLEGRO_VOICE *voice)
380 {
381 return voice->attached_stream->pos;
382 }
383
pulseaudio_set_voice_position(ALLEGRO_VOICE * voice,unsigned int pos)384 static int pulseaudio_set_voice_position(ALLEGRO_VOICE *voice, unsigned int pos)
385 {
386 PULSEAUDIO_VOICE *pv = voice->extra;
387
388 pa_simple_drain(pv->s, NULL);
389
390 al_lock_mutex(pv->buffer_mutex);
391 voice->attached_stream->pos = pos;
392 pv->buffer = (char *)voice->attached_stream->spl_data.buffer.ptr +
393 pos * pv->frame_size_in_bytes;
394 al_unlock_mutex(pv->buffer_mutex);
395
396 return 0;
397 }
398
399 /* Recording */
400
401 typedef struct PULSEAUDIO_RECORDER {
402 pa_simple *s;
403 pa_sample_spec ss;
404 pa_buffer_attr ba;
405 } PULSEAUDIO_RECORDER;
406
pulse_audio_update_recorder(ALLEGRO_THREAD * t,void * data)407 static void *pulse_audio_update_recorder(ALLEGRO_THREAD *t, void *data)
408 {
409 ALLEGRO_AUDIO_RECORDER *r = (ALLEGRO_AUDIO_RECORDER *) data;
410 PULSEAUDIO_RECORDER *pa = (PULSEAUDIO_RECORDER *) r->extra;
411 ALLEGRO_EVENT user_event;
412 uint8_t *null_buffer;
413 unsigned int fragment_i = 0;
414
415 null_buffer = al_malloc(1024);
416 if (!null_buffer) {
417 ALLEGRO_ERROR("Unable to create buffer for draining PulseAudio.\n");
418 return NULL;
419 }
420
421 while (!al_get_thread_should_stop(t))
422 {
423 al_lock_mutex(r->mutex);
424 if (!r->is_recording) {
425 /* Even if not recording, we still want to read from the PA server.
426 Otherwise it will buffer everything and spit it all out whenever
427 the recording resumes. */
428 al_unlock_mutex(r->mutex);
429 pa_simple_read(pa->s, null_buffer, 1024, NULL);
430 }
431 else {
432 ALLEGRO_AUDIO_RECORDER_EVENT *e;
433 al_unlock_mutex(r->mutex);
434 if (pa_simple_read(pa->s, r->fragments[fragment_i], r->fragment_size, NULL) >= 0) {
435 user_event.user.type = ALLEGRO_EVENT_AUDIO_RECORDER_FRAGMENT;
436 e = al_get_audio_recorder_event(&user_event);
437 e->buffer = r->fragments[fragment_i];
438 e->samples = r->samples;
439 al_emit_user_event(&r->source, &user_event, NULL);
440
441 if (++fragment_i == r->fragment_count) {
442 fragment_i = 0;
443 }
444 }
445 }
446 }
447
448 al_free(null_buffer);
449 return NULL;
450 };
451
pulseaudio_allocate_recorder(ALLEGRO_AUDIO_RECORDER * r)452 static int pulseaudio_allocate_recorder(ALLEGRO_AUDIO_RECORDER *r)
453 {
454 PULSEAUDIO_RECORDER *pa;
455
456 pa = al_calloc(1, sizeof(*pa));
457 if (!pa) {
458 ALLEGRO_ERROR("Unable to allocate memory for PULSEAUDIO_RECORDER.\n");
459 return 1;
460 }
461
462 pa->ss.channels = al_get_channel_count(r->chan_conf);
463 pa->ss.rate = r->frequency;
464
465 if (r->depth == ALLEGRO_AUDIO_DEPTH_UINT8)
466 pa->ss.format = PA_SAMPLE_U8;
467 else if (r->depth == ALLEGRO_AUDIO_DEPTH_INT16)
468 pa->ss.format = PA_SAMPLE_S16NE;
469 #if PA_API_VERSION > 11
470 else if (r->depth == ALLEGRO_AUDIO_DEPTH_INT24)
471 pa->ss.format = PA_SAMPLE_S24NE;
472 #endif
473 else if (r->depth == ALLEGRO_AUDIO_DEPTH_FLOAT32)
474 pa->ss.format = PA_SAMPLE_FLOAT32NE;
475 else {
476 ALLEGRO_ERROR("Unsupported PulseAudio sound format (depth).\n");
477 al_free(pa);
478 return 1;
479 }
480
481 /* maximum length of the PulseAudio buffer. -1 => let the server decide. */
482 pa->ba.maxlength = -1;
483
484 /* fragment size (bytes) controls how much data is returned back per read.
485 The documentation recommends -1 for default behavior, but that sets a
486 latency of around 2 seconds. Lower value decreases latency but increases
487 overhead.
488
489 The following attempts to set it (the base latency) to 1/8 of a second.
490 */
491 pa->ba.fragsize = (r->sample_size * r->frequency) / 8;
492
493 pa->s = pa_simple_new(NULL, al_get_app_name(), PA_STREAM_RECORD, NULL, "Allegro Audio Recorder", &pa->ss, NULL, &pa->ba, NULL);
494 if (!pa->s) {
495 ALLEGRO_ERROR("pa_simple_new() failed.\n");
496 al_free(pa);
497 return 1;
498 }
499
500 r->thread = al_create_thread(pulse_audio_update_recorder, r);
501 r->extra = pa;
502
503 return 0;
504 };
505
pulseaudio_deallocate_recorder(ALLEGRO_AUDIO_RECORDER * r)506 static void pulseaudio_deallocate_recorder(ALLEGRO_AUDIO_RECORDER *r)
507 {
508 PULSEAUDIO_RECORDER *pa = (PULSEAUDIO_RECORDER *) r->extra;
509
510 pa_simple_free(pa->s);
511 al_free(r->extra);
512 }
513
514 ALLEGRO_AUDIO_DRIVER _al_kcm_pulseaudio_driver =
515 {
516 "PulseAudio",
517
518 pulseaudio_open,
519 pulseaudio_close,
520
521 pulseaudio_allocate_voice,
522 pulseaudio_deallocate_voice,
523
524 pulseaudio_load_voice,
525 pulseaudio_unload_voice,
526
527 pulseaudio_start_voice,
528 pulseaudio_stop_voice,
529
530 pulseaudio_voice_is_playing,
531
532 pulseaudio_get_voice_position,
533 pulseaudio_set_voice_position,
534
535 pulseaudio_allocate_recorder,
536 pulseaudio_deallocate_recorder
537 };
538
539 /* vim: set sts=3 sw=3 et: */
540