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