1/* public domain */
2#include "qemu-common.h"
3#include "audio.h"
4
5#include <pulse/simple.h>
6#include <pulse/error.h>
7#include <dlfcn.h>
8
9#define AUDIO_CAP "pulseaudio"
10#include "audio_int.h"
11#include "audio_pt_int.h"
12
13#define DEBUG 1
14
15#if DEBUG
16#  include "android/qemu-debug.h"
17#  include <stdio.h>
18#  define D(...)  VERBOSE_PRINT(audio,__VA_ARGS__)
19#  define D_ACTIVE  VERBOSE_CHECK(audio)
20#  define O(...)  VERBOSE_PRINT(audioout,__VA_ARGS__)
21#  define I(...)  VERBOSE_PRINT(audioin,__VA_ARGS__)
22#else
23#  define D(...)  ((void)0)
24#  define D_ACTIVE 0
25#  define O(...)  ((void)0)
26#  define I(...)  ((void)0)
27#endif
28
29#define  DYNLINK_FUNCTIONS   \
30    DYNLINK_FUNC(pa_simple*,pa_simple_new,(const char* server,const char* name, pa_stream_direction_t dir, const char* dev, const char* stream_name, const pa_sample_spec* ss, const pa_channel_map* map, const pa_buffer_attr *attr, int *error)) \
31    DYNLINK_FUNC(void,pa_simple_free,(pa_simple* s))\
32    DYNLINK_FUNC(int,pa_simple_write,(pa_simple* s, const void* data, size_t bytes, int* error))\
33    DYNLINK_FUNC(int,pa_simple_read,(pa_simple* s,void* data, size_t bytes, int* error))\
34    DYNLINK_FUNC(const char*,pa_strerror,(int error))\
35
36#define DYNLINK_FUNCTIONS_INIT \
37    pa_dynlink_init
38
39static void* pa_lib;
40
41#include "android/dynlink.h"
42
43typedef struct {
44    HWVoiceOut hw;
45    int done;
46    int live;
47    int decr;
48    int rpos;
49    pa_simple *s;
50    void *pcm_buf;
51    struct audio_pt pt;
52} PAVoiceOut;
53
54typedef struct {
55    HWVoiceIn hw;
56    int done;
57    int dead;
58    int incr;
59    int wpos;
60    pa_simple *s;
61    void *pcm_buf;
62    struct audio_pt pt;
63} PAVoiceIn;
64
65static struct {
66    int samples;
67    int divisor;
68    char *server;
69    char *sink;
70    char *source;
71} conf = {
72    .samples = 1024,
73    .divisor = 2,
74};
75
76static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
77{
78    va_list ap;
79
80    va_start (ap, fmt);
81    AUD_vlog (AUDIO_CAP, fmt, ap);
82    va_end (ap);
83
84    AUD_log (AUDIO_CAP, "Reason: %s\n", FF(pa_strerror) (err));
85}
86
87static void *qpa_thread_out (void *arg)
88{
89    PAVoiceOut *pa = arg;
90    HWVoiceOut *hw = &pa->hw;
91    int threshold;
92
93    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
94
95    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
96        return NULL;
97    }
98
99    for (;;) {
100        int decr, to_mix, rpos;
101
102        for (;;) {
103            if (pa->done) {
104                goto exit;
105            }
106
107            if (pa->live > threshold) {
108                break;
109            }
110
111            if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
112                goto exit;
113            }
114        }
115
116        decr = to_mix = pa->live;
117        rpos = hw->rpos;
118
119        if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
120            return NULL;
121        }
122
123        while (to_mix) {
124            int error;
125            int chunk = audio_MIN (to_mix, hw->samples - rpos);
126            struct st_sample *src = hw->mix_buf + rpos;
127
128            hw->clip (pa->pcm_buf, src, chunk);
129
130            if (FF(pa_simple_write) (pa->s, pa->pcm_buf,
131                                 chunk << hw->info.shift, &error) < 0) {
132                qpa_logerr (error, "pa_simple_write failed\n");
133                return NULL;
134            }
135
136            rpos = (rpos + chunk) % hw->samples;
137            to_mix -= chunk;
138        }
139
140        if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
141            return NULL;
142        }
143
144        pa->rpos = rpos;
145        pa->live -= decr;
146        pa->decr += decr;
147    }
148
149 exit:
150    audio_pt_unlock (&pa->pt, AUDIO_FUNC);
151    return NULL;
152}
153
154static int qpa_run_out (HWVoiceOut *hw, int live)
155{
156    int decr;
157    PAVoiceOut *pa = (PAVoiceOut *) hw;
158
159    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
160        return 0;
161    }
162
163    decr = audio_MIN (live, pa->decr);
164    pa->decr -= decr;
165    pa->live = live - decr;
166    hw->rpos = pa->rpos;
167    if (pa->live > 0) {
168        audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
169    }
170    else {
171        audio_pt_unlock (&pa->pt, AUDIO_FUNC);
172    }
173    return decr;
174}
175
176static int qpa_write (SWVoiceOut *sw, void *buf, int len)
177{
178    return audio_pcm_sw_write (sw, buf, len);
179}
180
181/* capture */
182static void *qpa_thread_in (void *arg)
183{
184    PAVoiceIn *pa = arg;
185    HWVoiceIn *hw = &pa->hw;
186    int threshold;
187
188    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
189
190    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
191        return NULL;
192    }
193
194    for (;;) {
195        int incr, to_grab, wpos;
196
197        for (;;) {
198            if (pa->done) {
199                goto exit;
200            }
201
202            if (pa->dead > threshold) {
203                break;
204            }
205
206            if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
207                goto exit;
208            }
209        }
210
211        incr = to_grab = pa->dead;
212        wpos = hw->wpos;
213
214        if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
215            return NULL;
216        }
217
218        while (to_grab) {
219            int error;
220            int chunk = audio_MIN (to_grab, hw->samples - wpos);
221            void *buf = advance (pa->pcm_buf, wpos);
222
223            if (FF(pa_simple_read) (pa->s, buf,
224                                chunk << hw->info.shift, &error) < 0) {
225                qpa_logerr (error, "pa_simple_read failed\n");
226                return NULL;
227            }
228
229            hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume);
230            wpos = (wpos + chunk) % hw->samples;
231            to_grab -= chunk;
232        }
233
234        if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
235            return NULL;
236        }
237
238        pa->wpos = wpos;
239        pa->dead -= incr;
240        pa->incr += incr;
241    }
242
243 exit:
244    audio_pt_unlock (&pa->pt, AUDIO_FUNC);
245    return NULL;
246}
247
248static int qpa_run_in (HWVoiceIn *hw)
249{
250    int live, incr, dead;
251    PAVoiceIn *pa = (PAVoiceIn *) hw;
252
253    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
254        return 0;
255    }
256
257    live = audio_pcm_hw_get_live_in (hw);
258    dead = hw->samples - live;
259    incr = audio_MIN (dead, pa->incr);
260    pa->incr -= incr;
261    pa->dead = dead - incr;
262    hw->wpos = pa->wpos;
263    if (pa->dead > 0) {
264        audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
265    }
266    else {
267        audio_pt_unlock (&pa->pt, AUDIO_FUNC);
268    }
269    return incr;
270}
271
272static int qpa_read (SWVoiceIn *sw, void *buf, int len)
273{
274    return audio_pcm_sw_read (sw, buf, len);
275}
276
277static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
278{
279    int format;
280
281    switch (afmt) {
282    case AUD_FMT_S8:
283    case AUD_FMT_U8:
284        format = PA_SAMPLE_U8;
285        break;
286    case AUD_FMT_S16:
287    case AUD_FMT_U16:
288        format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
289        break;
290    case AUD_FMT_S32:
291    case AUD_FMT_U32:
292        format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
293        break;
294    default:
295        dolog ("Internal logic error: Bad audio format %d\n", afmt);
296        format = PA_SAMPLE_U8;
297        break;
298    }
299    return format;
300}
301
302static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
303{
304    switch (fmt) {
305    case PA_SAMPLE_U8:
306        return AUD_FMT_U8;
307    case PA_SAMPLE_S16BE:
308        *endianness = 1;
309        return AUD_FMT_S16;
310    case PA_SAMPLE_S16LE:
311        *endianness = 0;
312        return AUD_FMT_S16;
313    case PA_SAMPLE_S32BE:
314        *endianness = 1;
315        return AUD_FMT_S32;
316    case PA_SAMPLE_S32LE:
317        *endianness = 0;
318        return AUD_FMT_S32;
319    default:
320        dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
321        return AUD_FMT_U8;
322    }
323}
324
325static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
326{
327    int error;
328    static pa_sample_spec ss;
329    struct audsettings obt_as = *as;
330    PAVoiceOut *pa = (PAVoiceOut *) hw;
331
332    ss.format = audfmt_to_pa (as->fmt, as->endianness);
333    ss.channels = as->nchannels;
334    ss.rate = as->freq;
335
336    obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
337
338    pa->s = FF(pa_simple_new) (
339        conf.server,
340        "qemu",
341        PA_STREAM_PLAYBACK,
342        conf.sink,
343        "pcm.playback",
344        &ss,
345        NULL,                   /* channel map */
346        NULL,                   /* buffering attributes */
347        &error
348        );
349    if (!pa->s) {
350        qpa_logerr (error, "pa_simple_new for playback failed\n");
351        goto fail1;
352    }
353
354    audio_pcm_init_info (&hw->info, &obt_as);
355    hw->samples = conf.samples;
356    pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
357    if (!pa->pcm_buf) {
358        dolog ("Could not allocate buffer (%d bytes)\n",
359               hw->samples << hw->info.shift);
360        goto fail2;
361    }
362
363    if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
364        goto fail3;
365    }
366
367    return 0;
368
369 fail3:
370    g_free (pa->pcm_buf);
371    pa->pcm_buf = NULL;
372 fail2:
373    FF(pa_simple_free) (pa->s);
374    pa->s = NULL;
375 fail1:
376    return -1;
377}
378
379static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
380{
381    int error;
382    static pa_sample_spec ss;
383    struct audsettings obt_as = *as;
384    PAVoiceIn *pa = (PAVoiceIn *) hw;
385
386    ss.format = audfmt_to_pa (as->fmt, as->endianness);
387    ss.channels = as->nchannels;
388    ss.rate = as->freq;
389
390    obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
391
392    pa->s = FF(pa_simple_new) (
393        conf.server,
394        "qemu",
395        PA_STREAM_RECORD,
396        conf.source,
397        "pcm.capture",
398        &ss,
399        NULL,                   /* channel map */
400        NULL,                   /* buffering attributes */
401        &error
402        );
403    if (!pa->s) {
404        qpa_logerr (error, "pa_simple_new for capture failed\n");
405        goto fail1;
406    }
407
408    audio_pcm_init_info (&hw->info, &obt_as);
409    hw->samples = conf.samples;
410    pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
411    if (!pa->pcm_buf) {
412        dolog ("Could not allocate buffer (%d bytes)\n",
413               hw->samples << hw->info.shift);
414        goto fail2;
415    }
416
417    if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
418        goto fail3;
419    }
420
421    return 0;
422
423 fail3:
424    g_free (pa->pcm_buf);
425    pa->pcm_buf = NULL;
426 fail2:
427    FF(pa_simple_free) (pa->s);
428    pa->s = NULL;
429 fail1:
430    return -1;
431}
432
433static void qpa_fini_out (HWVoiceOut *hw)
434{
435    void *ret;
436    PAVoiceOut *pa = (PAVoiceOut *) hw;
437
438    audio_pt_lock (&pa->pt, AUDIO_FUNC);
439    pa->done = 1;
440    audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
441    audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
442
443    if (pa->s) {
444        FF(pa_simple_free) (pa->s);
445        pa->s = NULL;
446    }
447
448    audio_pt_fini (&pa->pt, AUDIO_FUNC);
449    g_free (pa->pcm_buf);
450    pa->pcm_buf = NULL;
451}
452
453static void qpa_fini_in (HWVoiceIn *hw)
454{
455    void *ret;
456    PAVoiceIn *pa = (PAVoiceIn *) hw;
457
458    audio_pt_lock (&pa->pt, AUDIO_FUNC);
459    pa->done = 1;
460    audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
461    audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
462
463    if (pa->s) {
464        FF(pa_simple_free) (pa->s);
465        pa->s = NULL;
466    }
467
468    audio_pt_fini (&pa->pt, AUDIO_FUNC);
469    g_free (pa->pcm_buf);
470    pa->pcm_buf = NULL;
471}
472
473static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
474{
475    (void) hw;
476    (void) cmd;
477    return 0;
478}
479
480static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
481{
482    (void) hw;
483    (void) cmd;
484    return 0;
485}
486
487/* common */
488static void *qpa_audio_init (void)
489{
490    void*  result = NULL;
491
492    D("%s: entering", __FUNCTION__);
493    pa_lib = dlopen( "libpulse-simple.so", RTLD_NOW );
494    if (pa_lib == NULL)
495        pa_lib = dlopen( "libpulse-simple.so.0", RTLD_NOW );
496
497    if (pa_lib == NULL) {
498        D("could not find libpulse on this system\n");
499        goto Exit;
500    }
501
502    if (pa_dynlink_init(pa_lib) < 0)
503        goto Fail;
504
505    {
506        pa_sample_spec  ss;
507        int             error;
508        pa_simple*      simple;
509
510        ss.format   = PA_SAMPLE_U8;
511        ss.rate     = 44100;
512        ss.channels = 1;
513
514        /* try to open it for playback */
515        simple = FF(pa_simple_new) (
516            conf.server,
517            "qemu",
518            PA_STREAM_PLAYBACK,
519            conf.sink,
520            "pcm.playback",
521            &ss,
522            NULL,                   /* channel map */
523            NULL,                   /* buffering attributes */
524            &error
525            );
526
527        if (simple == NULL) {
528            D("%s: error opening open pulse audio library: %s",
529              __FUNCTION__, FF(pa_strerror)(error));
530            goto Fail;
531        }
532        FF(pa_simple_free)(simple);
533    }
534
535    result = &conf;
536    goto Exit;
537
538Fail:
539    D("%s: failed to open library\n", __FUNCTION__);
540    dlclose(pa_lib);
541
542Exit:
543    D("%s: exiting", __FUNCTION__);
544    return result;
545}
546
547static void qpa_audio_fini (void *opaque)
548{
549    if (pa_lib != NULL) {
550        dlclose(pa_lib);
551        pa_lib = NULL;
552    }
553    (void) opaque;
554    (void) opaque;
555}
556
557struct audio_option qpa_options[] = {
558    {
559        .name  = "SAMPLES",
560        .tag   = AUD_OPT_INT,
561        .valp  = &conf.samples,
562        .descr = "buffer size in samples"
563    },
564    {
565        .name  = "DIVISOR",
566        .tag   = AUD_OPT_INT,
567        .valp  = &conf.divisor,
568        .descr = "threshold divisor"
569    },
570    {
571        .name  = "SERVER",
572        .tag   = AUD_OPT_STR,
573        .valp  = &conf.server,
574        .descr = "server address"
575    },
576    {
577        .name  = "SINK",
578        .tag   = AUD_OPT_STR,
579        .valp  = &conf.sink,
580        .descr = "sink device name"
581    },
582    {
583        .name  = "SOURCE",
584        .tag   = AUD_OPT_STR,
585        .valp  = &conf.source,
586        .descr = "source device name"
587    },
588    { /* End of list */ }
589};
590
591static struct audio_pcm_ops qpa_pcm_ops = {
592    .init_out = qpa_init_out,
593    .fini_out = qpa_fini_out,
594    .run_out  = qpa_run_out,
595    .write    = qpa_write,
596    .ctl_out  = qpa_ctl_out,
597
598    .init_in  = qpa_init_in,
599    .fini_in  = qpa_fini_in,
600    .run_in   = qpa_run_in,
601    .read     = qpa_read,
602    .ctl_in   = qpa_ctl_in
603};
604
605struct audio_driver pa_audio_driver = {
606    .name           = "pa",
607    .descr          = "http://www.pulseaudio.org/",
608    .options        = qpa_options,
609    .init           = qpa_audio_init,
610    .fini           = qpa_audio_fini,
611    .pcm_ops        = &qpa_pcm_ops,
612    .can_be_default = 1,
613    .max_voices_out = INT_MAX,
614    .max_voices_in  = INT_MAX,
615    .voice_size_out = sizeof (PAVoiceOut),
616    .voice_size_in  = sizeof (PAVoiceIn)
617};
618