Statistics
| Branch: | Revision:

root / audio / paaudio.c @ 45724d6d

History | View | Annotate | Download (23.6 kB)

1 b8e59f18 malc
/* public domain */
2 b8e59f18 malc
#include "qemu-common.h"
3 b8e59f18 malc
#include "audio.h"
4 b8e59f18 malc
5 ea9ebc2c Marc-André Lureau
#include <pulse/pulseaudio.h>
6 b8e59f18 malc
7 b8e59f18 malc
#define AUDIO_CAP "pulseaudio"
8 b8e59f18 malc
#include "audio_int.h"
9 b8e59f18 malc
#include "audio_pt_int.h"
10 b8e59f18 malc
11 b8e59f18 malc
typedef struct {
12 b8e59f18 malc
    HWVoiceOut hw;
13 b8e59f18 malc
    int done;
14 b8e59f18 malc
    int live;
15 b8e59f18 malc
    int decr;
16 b8e59f18 malc
    int rpos;
17 ea9ebc2c Marc-André Lureau
    pa_stream *stream;
18 b8e59f18 malc
    void *pcm_buf;
19 b8e59f18 malc
    struct audio_pt pt;
20 b8e59f18 malc
} PAVoiceOut;
21 b8e59f18 malc
22 b8e59f18 malc
typedef struct {
23 b8e59f18 malc
    HWVoiceIn hw;
24 b8e59f18 malc
    int done;
25 b8e59f18 malc
    int dead;
26 b8e59f18 malc
    int incr;
27 b8e59f18 malc
    int wpos;
28 ea9ebc2c Marc-André Lureau
    pa_stream *stream;
29 b8e59f18 malc
    void *pcm_buf;
30 b8e59f18 malc
    struct audio_pt pt;
31 ea9ebc2c Marc-André Lureau
    const void *read_data;
32 ea9ebc2c Marc-André Lureau
    size_t read_index, read_length;
33 b8e59f18 malc
} PAVoiceIn;
34 b8e59f18 malc
35 ea9ebc2c Marc-André Lureau
typedef struct {
36 b8e59f18 malc
    int samples;
37 b8e59f18 malc
    char *server;
38 b8e59f18 malc
    char *sink;
39 b8e59f18 malc
    char *source;
40 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop *mainloop;
41 ea9ebc2c Marc-André Lureau
    pa_context *context;
42 ea9ebc2c Marc-André Lureau
} paaudio;
43 ea9ebc2c Marc-André Lureau
44 ea9ebc2c Marc-André Lureau
static paaudio glob_paaudio = {
45 bf1064b5 Gerd Hoffmann
    .samples = 4096,
46 b8e59f18 malc
};
47 b8e59f18 malc
48 b8e59f18 malc
static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
49 b8e59f18 malc
{
50 b8e59f18 malc
    va_list ap;
51 b8e59f18 malc
52 b8e59f18 malc
    va_start (ap, fmt);
53 b8e59f18 malc
    AUD_vlog (AUDIO_CAP, fmt, ap);
54 b8e59f18 malc
    va_end (ap);
55 b8e59f18 malc
56 b8e59f18 malc
    AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
57 b8e59f18 malc
}
58 b8e59f18 malc
59 8f473dd1 Gerd Hoffmann
#ifndef PA_CONTEXT_IS_GOOD
60 8f473dd1 Gerd Hoffmann
static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
61 8f473dd1 Gerd Hoffmann
{
62 8f473dd1 Gerd Hoffmann
    return
63 8f473dd1 Gerd Hoffmann
        x == PA_CONTEXT_CONNECTING ||
64 8f473dd1 Gerd Hoffmann
        x == PA_CONTEXT_AUTHORIZING ||
65 8f473dd1 Gerd Hoffmann
        x == PA_CONTEXT_SETTING_NAME ||
66 8f473dd1 Gerd Hoffmann
        x == PA_CONTEXT_READY;
67 8f473dd1 Gerd Hoffmann
}
68 8f473dd1 Gerd Hoffmann
#endif
69 8f473dd1 Gerd Hoffmann
70 8f473dd1 Gerd Hoffmann
#ifndef PA_STREAM_IS_GOOD
71 8f473dd1 Gerd Hoffmann
static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
72 8f473dd1 Gerd Hoffmann
{
73 8f473dd1 Gerd Hoffmann
    return
74 8f473dd1 Gerd Hoffmann
        x == PA_STREAM_CREATING ||
75 8f473dd1 Gerd Hoffmann
        x == PA_STREAM_READY;
76 8f473dd1 Gerd Hoffmann
}
77 8f473dd1 Gerd Hoffmann
#endif
78 8f473dd1 Gerd Hoffmann
79 ea9ebc2c Marc-André Lureau
#define CHECK_SUCCESS_GOTO(c, rerror, expression, label)        \
80 ea9ebc2c Marc-André Lureau
    do {                                                        \
81 ea9ebc2c Marc-André Lureau
        if (!(expression)) {                                    \
82 ea9ebc2c Marc-André Lureau
            if (rerror) {                                       \
83 ea9ebc2c Marc-André Lureau
                *(rerror) = pa_context_errno ((c)->context);    \
84 ea9ebc2c Marc-André Lureau
            }                                                   \
85 ea9ebc2c Marc-André Lureau
            goto label;                                         \
86 ea9ebc2c Marc-André Lureau
        }                                                       \
87 ea9ebc2c Marc-André Lureau
    } while (0);
88 ea9ebc2c Marc-André Lureau
89 ea9ebc2c Marc-André Lureau
#define CHECK_DEAD_GOTO(c, stream, rerror, label)                       \
90 ea9ebc2c Marc-André Lureau
    do {                                                                \
91 ea9ebc2c Marc-André Lureau
        if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
92 ea9ebc2c Marc-André Lureau
            !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
93 ea9ebc2c Marc-André Lureau
            if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
94 ea9ebc2c Marc-André Lureau
                ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
95 ea9ebc2c Marc-André Lureau
                if (rerror) {                                           \
96 ea9ebc2c Marc-André Lureau
                    *(rerror) = pa_context_errno ((c)->context);        \
97 ea9ebc2c Marc-André Lureau
                }                                                       \
98 ea9ebc2c Marc-André Lureau
            } else {                                                    \
99 ea9ebc2c Marc-André Lureau
                if (rerror) {                                           \
100 ea9ebc2c Marc-André Lureau
                    *(rerror) = PA_ERR_BADSTATE;                        \
101 ea9ebc2c Marc-André Lureau
                }                                                       \
102 ea9ebc2c Marc-André Lureau
            }                                                           \
103 ea9ebc2c Marc-André Lureau
            goto label;                                                 \
104 ea9ebc2c Marc-André Lureau
        }                                                               \
105 ea9ebc2c Marc-André Lureau
    } while (0);
106 ea9ebc2c Marc-André Lureau
107 ea9ebc2c Marc-André Lureau
static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
108 ea9ebc2c Marc-André Lureau
{
109 ea9ebc2c Marc-André Lureau
    paaudio *g = &glob_paaudio;
110 ea9ebc2c Marc-André Lureau
111 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop_lock (g->mainloop);
112 ea9ebc2c Marc-André Lureau
113 ea9ebc2c Marc-André Lureau
    CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
114 ea9ebc2c Marc-André Lureau
115 ea9ebc2c Marc-André Lureau
    while (length > 0) {
116 ea9ebc2c Marc-André Lureau
        size_t l;
117 ea9ebc2c Marc-André Lureau
118 ea9ebc2c Marc-André Lureau
        while (!p->read_data) {
119 ea9ebc2c Marc-André Lureau
            int r;
120 ea9ebc2c Marc-André Lureau
121 ea9ebc2c Marc-André Lureau
            r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
122 ea9ebc2c Marc-André Lureau
            CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
123 ea9ebc2c Marc-André Lureau
124 ea9ebc2c Marc-André Lureau
            if (!p->read_data) {
125 ea9ebc2c Marc-André Lureau
                pa_threaded_mainloop_wait (g->mainloop);
126 ea9ebc2c Marc-André Lureau
                CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
127 ea9ebc2c Marc-André Lureau
            } else {
128 ea9ebc2c Marc-André Lureau
                p->read_index = 0;
129 ea9ebc2c Marc-André Lureau
            }
130 ea9ebc2c Marc-André Lureau
        }
131 ea9ebc2c Marc-André Lureau
132 ea9ebc2c Marc-André Lureau
        l = p->read_length < length ? p->read_length : length;
133 ea9ebc2c Marc-André Lureau
        memcpy (data, (const uint8_t *) p->read_data+p->read_index, l);
134 ea9ebc2c Marc-André Lureau
135 ea9ebc2c Marc-André Lureau
        data = (uint8_t *) data + l;
136 ea9ebc2c Marc-André Lureau
        length -= l;
137 ea9ebc2c Marc-André Lureau
138 ea9ebc2c Marc-André Lureau
        p->read_index += l;
139 ea9ebc2c Marc-André Lureau
        p->read_length -= l;
140 ea9ebc2c Marc-André Lureau
141 ea9ebc2c Marc-André Lureau
        if (!p->read_length) {
142 ea9ebc2c Marc-André Lureau
            int r;
143 ea9ebc2c Marc-André Lureau
144 ea9ebc2c Marc-André Lureau
            r = pa_stream_drop (p->stream);
145 ea9ebc2c Marc-André Lureau
            p->read_data = NULL;
146 ea9ebc2c Marc-André Lureau
            p->read_length = 0;
147 ea9ebc2c Marc-André Lureau
            p->read_index = 0;
148 ea9ebc2c Marc-André Lureau
149 ea9ebc2c Marc-André Lureau
            CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
150 ea9ebc2c Marc-André Lureau
        }
151 ea9ebc2c Marc-André Lureau
    }
152 ea9ebc2c Marc-André Lureau
153 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop_unlock (g->mainloop);
154 ea9ebc2c Marc-André Lureau
    return 0;
155 ea9ebc2c Marc-André Lureau
156 ea9ebc2c Marc-André Lureau
unlock_and_fail:
157 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop_unlock (g->mainloop);
158 ea9ebc2c Marc-André Lureau
    return -1;
159 ea9ebc2c Marc-André Lureau
}
160 ea9ebc2c Marc-André Lureau
161 ea9ebc2c Marc-André Lureau
static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
162 ea9ebc2c Marc-André Lureau
{
163 ea9ebc2c Marc-André Lureau
    paaudio *g = &glob_paaudio;
164 ea9ebc2c Marc-André Lureau
165 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop_lock (g->mainloop);
166 ea9ebc2c Marc-André Lureau
167 ea9ebc2c Marc-André Lureau
    CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
168 ea9ebc2c Marc-André Lureau
169 ea9ebc2c Marc-André Lureau
    while (length > 0) {
170 ea9ebc2c Marc-André Lureau
        size_t l;
171 ea9ebc2c Marc-André Lureau
        int r;
172 ea9ebc2c Marc-André Lureau
173 ea9ebc2c Marc-André Lureau
        while (!(l = pa_stream_writable_size (p->stream))) {
174 ea9ebc2c Marc-André Lureau
            pa_threaded_mainloop_wait (g->mainloop);
175 ea9ebc2c Marc-André Lureau
            CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
176 ea9ebc2c Marc-André Lureau
        }
177 ea9ebc2c Marc-André Lureau
178 ea9ebc2c Marc-André Lureau
        CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail);
179 ea9ebc2c Marc-André Lureau
180 ea9ebc2c Marc-André Lureau
        if (l > length) {
181 ea9ebc2c Marc-André Lureau
            l = length;
182 ea9ebc2c Marc-André Lureau
        }
183 ea9ebc2c Marc-André Lureau
184 ea9ebc2c Marc-André Lureau
        r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
185 ea9ebc2c Marc-André Lureau
        CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail);
186 ea9ebc2c Marc-André Lureau
187 ea9ebc2c Marc-André Lureau
        data = (const uint8_t *) data + l;
188 ea9ebc2c Marc-André Lureau
        length -= l;
189 ea9ebc2c Marc-André Lureau
    }
190 ea9ebc2c Marc-André Lureau
191 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop_unlock (g->mainloop);
192 ea9ebc2c Marc-André Lureau
    return 0;
193 ea9ebc2c Marc-André Lureau
194 ea9ebc2c Marc-André Lureau
unlock_and_fail:
195 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop_unlock (g->mainloop);
196 ea9ebc2c Marc-André Lureau
    return -1;
197 ea9ebc2c Marc-André Lureau
}
198 ea9ebc2c Marc-André Lureau
199 b8e59f18 malc
static void *qpa_thread_out (void *arg)
200 b8e59f18 malc
{
201 b8e59f18 malc
    PAVoiceOut *pa = arg;
202 b8e59f18 malc
    HWVoiceOut *hw = &pa->hw;
203 b8e59f18 malc
204 b8e59f18 malc
    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
205 b8e59f18 malc
        return NULL;
206 b8e59f18 malc
    }
207 b8e59f18 malc
208 b8e59f18 malc
    for (;;) {
209 b8e59f18 malc
        int decr, to_mix, rpos;
210 b8e59f18 malc
211 b8e59f18 malc
        for (;;) {
212 b8e59f18 malc
            if (pa->done) {
213 b8e59f18 malc
                goto exit;
214 b8e59f18 malc
            }
215 b8e59f18 malc
216 6315633b Gerd Hoffmann
            if (pa->live > 0) {
217 b8e59f18 malc
                break;
218 b8e59f18 malc
            }
219 b8e59f18 malc
220 b8e59f18 malc
            if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
221 b8e59f18 malc
                goto exit;
222 b8e59f18 malc
            }
223 b8e59f18 malc
        }
224 b8e59f18 malc
225 ea9ebc2c Marc-André Lureau
        decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2);
226 6315633b Gerd Hoffmann
        rpos = pa->rpos;
227 b8e59f18 malc
228 b8e59f18 malc
        if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
229 b8e59f18 malc
            return NULL;
230 b8e59f18 malc
        }
231 b8e59f18 malc
232 b8e59f18 malc
        while (to_mix) {
233 b8e59f18 malc
            int error;
234 b8e59f18 malc
            int chunk = audio_MIN (to_mix, hw->samples - rpos);
235 1ea879e5 malc
            struct st_sample *src = hw->mix_buf + rpos;
236 b8e59f18 malc
237 b8e59f18 malc
            hw->clip (pa->pcm_buf, src, chunk);
238 b8e59f18 malc
239 ea9ebc2c Marc-André Lureau
            if (qpa_simple_write (pa, pa->pcm_buf,
240 ea9ebc2c Marc-André Lureau
                                  chunk << hw->info.shift, &error) < 0) {
241 b8e59f18 malc
                qpa_logerr (error, "pa_simple_write failed\n");
242 b8e59f18 malc
                return NULL;
243 b8e59f18 malc
            }
244 b8e59f18 malc
245 b8e59f18 malc
            rpos = (rpos + chunk) % hw->samples;
246 b8e59f18 malc
            to_mix -= chunk;
247 b8e59f18 malc
        }
248 b8e59f18 malc
249 b8e59f18 malc
        if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
250 b8e59f18 malc
            return NULL;
251 b8e59f18 malc
        }
252 b8e59f18 malc
253 b8e59f18 malc
        pa->rpos = rpos;
254 6315633b Gerd Hoffmann
        pa->live -= decr;
255 b8e59f18 malc
        pa->decr += decr;
256 b8e59f18 malc
    }
257 b8e59f18 malc
258 b8e59f18 malc
 exit:
259 b8e59f18 malc
    audio_pt_unlock (&pa->pt, AUDIO_FUNC);
260 b8e59f18 malc
    return NULL;
261 b8e59f18 malc
}
262 b8e59f18 malc
263 bdff253c malc
static int qpa_run_out (HWVoiceOut *hw, int live)
264 b8e59f18 malc
{
265 bdff253c malc
    int decr;
266 b8e59f18 malc
    PAVoiceOut *pa = (PAVoiceOut *) hw;
267 b8e59f18 malc
268 b8e59f18 malc
    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
269 b8e59f18 malc
        return 0;
270 b8e59f18 malc
    }
271 b8e59f18 malc
272 b8e59f18 malc
    decr = audio_MIN (live, pa->decr);
273 b8e59f18 malc
    pa->decr -= decr;
274 b8e59f18 malc
    pa->live = live - decr;
275 b8e59f18 malc
    hw->rpos = pa->rpos;
276 b8e59f18 malc
    if (pa->live > 0) {
277 b8e59f18 malc
        audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
278 b8e59f18 malc
    }
279 b8e59f18 malc
    else {
280 b8e59f18 malc
        audio_pt_unlock (&pa->pt, AUDIO_FUNC);
281 b8e59f18 malc
    }
282 b8e59f18 malc
    return decr;
283 b8e59f18 malc
}
284 b8e59f18 malc
285 b8e59f18 malc
static int qpa_write (SWVoiceOut *sw, void *buf, int len)
286 b8e59f18 malc
{
287 b8e59f18 malc
    return audio_pcm_sw_write (sw, buf, len);
288 b8e59f18 malc
}
289 b8e59f18 malc
290 b8e59f18 malc
/* capture */
291 b8e59f18 malc
static void *qpa_thread_in (void *arg)
292 b8e59f18 malc
{
293 b8e59f18 malc
    PAVoiceIn *pa = arg;
294 b8e59f18 malc
    HWVoiceIn *hw = &pa->hw;
295 b8e59f18 malc
296 b8e59f18 malc
    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
297 b8e59f18 malc
        return NULL;
298 b8e59f18 malc
    }
299 b8e59f18 malc
300 b8e59f18 malc
    for (;;) {
301 b8e59f18 malc
        int incr, to_grab, wpos;
302 b8e59f18 malc
303 b8e59f18 malc
        for (;;) {
304 b8e59f18 malc
            if (pa->done) {
305 b8e59f18 malc
                goto exit;
306 b8e59f18 malc
            }
307 b8e59f18 malc
308 6315633b Gerd Hoffmann
            if (pa->dead > 0) {
309 b8e59f18 malc
                break;
310 b8e59f18 malc
            }
311 b8e59f18 malc
312 b8e59f18 malc
            if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
313 b8e59f18 malc
                goto exit;
314 b8e59f18 malc
            }
315 b8e59f18 malc
        }
316 b8e59f18 malc
317 ea9ebc2c Marc-André Lureau
        incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2);
318 6315633b Gerd Hoffmann
        wpos = pa->wpos;
319 b8e59f18 malc
320 b8e59f18 malc
        if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
321 b8e59f18 malc
            return NULL;
322 b8e59f18 malc
        }
323 b8e59f18 malc
324 b8e59f18 malc
        while (to_grab) {
325 b8e59f18 malc
            int error;
326 b8e59f18 malc
            int chunk = audio_MIN (to_grab, hw->samples - wpos);
327 b8e59f18 malc
            void *buf = advance (pa->pcm_buf, wpos);
328 b8e59f18 malc
329 ea9ebc2c Marc-André Lureau
            if (qpa_simple_read (pa, buf,
330 ea9ebc2c Marc-André Lureau
                                 chunk << hw->info.shift, &error) < 0) {
331 b8e59f18 malc
                qpa_logerr (error, "pa_simple_read failed\n");
332 b8e59f18 malc
                return NULL;
333 b8e59f18 malc
            }
334 b8e59f18 malc
335 00e07679 Michael Walle
            hw->conv (hw->conv_buf + wpos, buf, chunk);
336 b8e59f18 malc
            wpos = (wpos + chunk) % hw->samples;
337 b8e59f18 malc
            to_grab -= chunk;
338 b8e59f18 malc
        }
339 b8e59f18 malc
340 b8e59f18 malc
        if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
341 b8e59f18 malc
            return NULL;
342 b8e59f18 malc
        }
343 b8e59f18 malc
344 b8e59f18 malc
        pa->wpos = wpos;
345 b8e59f18 malc
        pa->dead -= incr;
346 b8e59f18 malc
        pa->incr += incr;
347 b8e59f18 malc
    }
348 b8e59f18 malc
349 b8e59f18 malc
 exit:
350 b8e59f18 malc
    audio_pt_unlock (&pa->pt, AUDIO_FUNC);
351 b8e59f18 malc
    return NULL;
352 b8e59f18 malc
}
353 b8e59f18 malc
354 b8e59f18 malc
static int qpa_run_in (HWVoiceIn *hw)
355 b8e59f18 malc
{
356 b8e59f18 malc
    int live, incr, dead;
357 b8e59f18 malc
    PAVoiceIn *pa = (PAVoiceIn *) hw;
358 b8e59f18 malc
359 b8e59f18 malc
    if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
360 b8e59f18 malc
        return 0;
361 b8e59f18 malc
    }
362 b8e59f18 malc
363 b8e59f18 malc
    live = audio_pcm_hw_get_live_in (hw);
364 b8e59f18 malc
    dead = hw->samples - live;
365 b8e59f18 malc
    incr = audio_MIN (dead, pa->incr);
366 b8e59f18 malc
    pa->incr -= incr;
367 b8e59f18 malc
    pa->dead = dead - incr;
368 b8e59f18 malc
    hw->wpos = pa->wpos;
369 b8e59f18 malc
    if (pa->dead > 0) {
370 b8e59f18 malc
        audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
371 b8e59f18 malc
    }
372 b8e59f18 malc
    else {
373 b8e59f18 malc
        audio_pt_unlock (&pa->pt, AUDIO_FUNC);
374 b8e59f18 malc
    }
375 b8e59f18 malc
    return incr;
376 b8e59f18 malc
}
377 b8e59f18 malc
378 b8e59f18 malc
static int qpa_read (SWVoiceIn *sw, void *buf, int len)
379 b8e59f18 malc
{
380 b8e59f18 malc
    return audio_pcm_sw_read (sw, buf, len);
381 b8e59f18 malc
}
382 b8e59f18 malc
383 b8e59f18 malc
static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness)
384 b8e59f18 malc
{
385 b8e59f18 malc
    int format;
386 b8e59f18 malc
387 b8e59f18 malc
    switch (afmt) {
388 b8e59f18 malc
    case AUD_FMT_S8:
389 b8e59f18 malc
    case AUD_FMT_U8:
390 b8e59f18 malc
        format = PA_SAMPLE_U8;
391 b8e59f18 malc
        break;
392 b8e59f18 malc
    case AUD_FMT_S16:
393 b8e59f18 malc
    case AUD_FMT_U16:
394 b8e59f18 malc
        format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
395 b8e59f18 malc
        break;
396 b8e59f18 malc
    case AUD_FMT_S32:
397 b8e59f18 malc
    case AUD_FMT_U32:
398 b8e59f18 malc
        format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
399 b8e59f18 malc
        break;
400 b8e59f18 malc
    default:
401 b8e59f18 malc
        dolog ("Internal logic error: Bad audio format %d\n", afmt);
402 b8e59f18 malc
        format = PA_SAMPLE_U8;
403 b8e59f18 malc
        break;
404 b8e59f18 malc
    }
405 b8e59f18 malc
    return format;
406 b8e59f18 malc
}
407 b8e59f18 malc
408 b8e59f18 malc
static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
409 b8e59f18 malc
{
410 b8e59f18 malc
    switch (fmt) {
411 b8e59f18 malc
    case PA_SAMPLE_U8:
412 b8e59f18 malc
        return AUD_FMT_U8;
413 b8e59f18 malc
    case PA_SAMPLE_S16BE:
414 b8e59f18 malc
        *endianness = 1;
415 b8e59f18 malc
        return AUD_FMT_S16;
416 b8e59f18 malc
    case PA_SAMPLE_S16LE:
417 b8e59f18 malc
        *endianness = 0;
418 b8e59f18 malc
        return AUD_FMT_S16;
419 b8e59f18 malc
    case PA_SAMPLE_S32BE:
420 b8e59f18 malc
        *endianness = 1;
421 b8e59f18 malc
        return AUD_FMT_S32;
422 b8e59f18 malc
    case PA_SAMPLE_S32LE:
423 b8e59f18 malc
        *endianness = 0;
424 b8e59f18 malc
        return AUD_FMT_S32;
425 b8e59f18 malc
    default:
426 b8e59f18 malc
        dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
427 b8e59f18 malc
        return AUD_FMT_U8;
428 b8e59f18 malc
    }
429 b8e59f18 malc
}
430 b8e59f18 malc
431 ea9ebc2c Marc-André Lureau
static void context_state_cb (pa_context *c, void *userdata)
432 ea9ebc2c Marc-André Lureau
{
433 ea9ebc2c Marc-André Lureau
    paaudio *g = &glob_paaudio;
434 ea9ebc2c Marc-André Lureau
435 ea9ebc2c Marc-André Lureau
    switch (pa_context_get_state(c)) {
436 ea9ebc2c Marc-André Lureau
    case PA_CONTEXT_READY:
437 ea9ebc2c Marc-André Lureau
    case PA_CONTEXT_TERMINATED:
438 ea9ebc2c Marc-André Lureau
    case PA_CONTEXT_FAILED:
439 ea9ebc2c Marc-André Lureau
        pa_threaded_mainloop_signal (g->mainloop, 0);
440 ea9ebc2c Marc-André Lureau
        break;
441 ea9ebc2c Marc-André Lureau
442 ea9ebc2c Marc-André Lureau
    case PA_CONTEXT_UNCONNECTED:
443 ea9ebc2c Marc-André Lureau
    case PA_CONTEXT_CONNECTING:
444 ea9ebc2c Marc-André Lureau
    case PA_CONTEXT_AUTHORIZING:
445 ea9ebc2c Marc-André Lureau
    case PA_CONTEXT_SETTING_NAME:
446 ea9ebc2c Marc-André Lureau
        break;
447 ea9ebc2c Marc-André Lureau
    }
448 ea9ebc2c Marc-André Lureau
}
449 ea9ebc2c Marc-André Lureau
450 ea9ebc2c Marc-André Lureau
static void stream_state_cb (pa_stream *s, void * userdata)
451 ea9ebc2c Marc-André Lureau
{
452 ea9ebc2c Marc-André Lureau
    paaudio *g = &glob_paaudio;
453 ea9ebc2c Marc-André Lureau
454 ea9ebc2c Marc-André Lureau
    switch (pa_stream_get_state (s)) {
455 ea9ebc2c Marc-André Lureau
456 ea9ebc2c Marc-André Lureau
    case PA_STREAM_READY:
457 ea9ebc2c Marc-André Lureau
    case PA_STREAM_FAILED:
458 ea9ebc2c Marc-André Lureau
    case PA_STREAM_TERMINATED:
459 ea9ebc2c Marc-André Lureau
        pa_threaded_mainloop_signal (g->mainloop, 0);
460 ea9ebc2c Marc-André Lureau
        break;
461 ea9ebc2c Marc-André Lureau
462 ea9ebc2c Marc-André Lureau
    case PA_STREAM_UNCONNECTED:
463 ea9ebc2c Marc-André Lureau
    case PA_STREAM_CREATING:
464 ea9ebc2c Marc-André Lureau
        break;
465 ea9ebc2c Marc-André Lureau
    }
466 ea9ebc2c Marc-André Lureau
}
467 ea9ebc2c Marc-André Lureau
468 ea9ebc2c Marc-André Lureau
static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
469 ea9ebc2c Marc-André Lureau
{
470 ea9ebc2c Marc-André Lureau
    paaudio *g = &glob_paaudio;
471 ea9ebc2c Marc-André Lureau
472 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop_signal (g->mainloop, 0);
473 ea9ebc2c Marc-André Lureau
}
474 ea9ebc2c Marc-André Lureau
475 ea9ebc2c Marc-André Lureau
static pa_stream *qpa_simple_new (
476 ea9ebc2c Marc-André Lureau
        const char *server,
477 ea9ebc2c Marc-André Lureau
        const char *name,
478 ea9ebc2c Marc-André Lureau
        pa_stream_direction_t dir,
479 ea9ebc2c Marc-André Lureau
        const char *dev,
480 ea9ebc2c Marc-André Lureau
        const char *stream_name,
481 ea9ebc2c Marc-André Lureau
        const pa_sample_spec *ss,
482 ea9ebc2c Marc-André Lureau
        const pa_channel_map *map,
483 ea9ebc2c Marc-André Lureau
        const pa_buffer_attr *attr,
484 ea9ebc2c Marc-André Lureau
        int *rerror)
485 ea9ebc2c Marc-André Lureau
{
486 ea9ebc2c Marc-André Lureau
    paaudio *g = &glob_paaudio;
487 ea9ebc2c Marc-André Lureau
    int r;
488 ea9ebc2c Marc-André Lureau
    pa_stream *stream;
489 ea9ebc2c Marc-André Lureau
490 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop_lock (g->mainloop);
491 ea9ebc2c Marc-André Lureau
492 ea9ebc2c Marc-André Lureau
    stream = pa_stream_new (g->context, name, ss, map);
493 ea9ebc2c Marc-André Lureau
    if (!stream) {
494 ea9ebc2c Marc-André Lureau
        goto fail;
495 ea9ebc2c Marc-André Lureau
    }
496 ea9ebc2c Marc-André Lureau
497 ea9ebc2c Marc-André Lureau
    pa_stream_set_state_callback (stream, stream_state_cb, g);
498 ea9ebc2c Marc-André Lureau
    pa_stream_set_read_callback (stream, stream_request_cb, g);
499 ea9ebc2c Marc-André Lureau
    pa_stream_set_write_callback (stream, stream_request_cb, g);
500 ea9ebc2c Marc-André Lureau
501 ea9ebc2c Marc-André Lureau
    if (dir == PA_STREAM_PLAYBACK) {
502 ea9ebc2c Marc-André Lureau
        r = pa_stream_connect_playback (stream, dev, attr,
503 ea9ebc2c Marc-André Lureau
                                        PA_STREAM_INTERPOLATE_TIMING
504 8f473dd1 Gerd Hoffmann
#ifdef PA_STREAM_ADJUST_LATENCY
505 ea9ebc2c Marc-André Lureau
                                        |PA_STREAM_ADJUST_LATENCY
506 8f473dd1 Gerd Hoffmann
#endif
507 ea9ebc2c Marc-André Lureau
                                        |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
508 ea9ebc2c Marc-André Lureau
    } else {
509 ea9ebc2c Marc-André Lureau
        r = pa_stream_connect_record (stream, dev, attr,
510 ea9ebc2c Marc-André Lureau
                                      PA_STREAM_INTERPOLATE_TIMING
511 8f473dd1 Gerd Hoffmann
#ifdef PA_STREAM_ADJUST_LATENCY
512 ea9ebc2c Marc-André Lureau
                                      |PA_STREAM_ADJUST_LATENCY
513 8f473dd1 Gerd Hoffmann
#endif
514 ea9ebc2c Marc-André Lureau
                                      |PA_STREAM_AUTO_TIMING_UPDATE);
515 ea9ebc2c Marc-André Lureau
    }
516 ea9ebc2c Marc-André Lureau
517 ea9ebc2c Marc-André Lureau
    if (r < 0) {
518 ea9ebc2c Marc-André Lureau
      goto fail;
519 ea9ebc2c Marc-André Lureau
    }
520 ea9ebc2c Marc-André Lureau
521 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop_unlock (g->mainloop);
522 ea9ebc2c Marc-André Lureau
523 ea9ebc2c Marc-André Lureau
    return stream;
524 ea9ebc2c Marc-André Lureau
525 ea9ebc2c Marc-André Lureau
fail:
526 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop_unlock (g->mainloop);
527 ea9ebc2c Marc-André Lureau
528 ea9ebc2c Marc-André Lureau
    if (stream) {
529 ea9ebc2c Marc-André Lureau
        pa_stream_unref (stream);
530 ea9ebc2c Marc-André Lureau
    }
531 ea9ebc2c Marc-André Lureau
532 d6c05bbf Gerd Hoffmann
    *rerror = pa_context_errno (g->context);
533 ea9ebc2c Marc-André Lureau
534 ea9ebc2c Marc-André Lureau
    return NULL;
535 ea9ebc2c Marc-André Lureau
}
536 ea9ebc2c Marc-André Lureau
537 1ea879e5 malc
static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
538 b8e59f18 malc
{
539 b8e59f18 malc
    int error;
540 b8e59f18 malc
    static pa_sample_spec ss;
541 e6d16fa4 Gerd Hoffmann
    static pa_buffer_attr ba;
542 1ea879e5 malc
    struct audsettings obt_as = *as;
543 b8e59f18 malc
    PAVoiceOut *pa = (PAVoiceOut *) hw;
544 b8e59f18 malc
545 b8e59f18 malc
    ss.format = audfmt_to_pa (as->fmt, as->endianness);
546 b8e59f18 malc
    ss.channels = as->nchannels;
547 b8e59f18 malc
    ss.rate = as->freq;
548 b8e59f18 malc
549 e6d16fa4 Gerd Hoffmann
    /*
550 e6d16fa4 Gerd Hoffmann
     * qemu audio tick runs at 250 Hz (by default), so processing
551 e6d16fa4 Gerd Hoffmann
     * data chunks worth 4 ms of sound should be a good fit.
552 e6d16fa4 Gerd Hoffmann
     */
553 e6d16fa4 Gerd Hoffmann
    ba.tlength = pa_usec_to_bytes (4 * 1000, &ss);
554 e6d16fa4 Gerd Hoffmann
    ba.minreq = pa_usec_to_bytes (2 * 1000, &ss);
555 e6d16fa4 Gerd Hoffmann
    ba.maxlength = -1;
556 e6d16fa4 Gerd Hoffmann
    ba.prebuf = -1;
557 e6d16fa4 Gerd Hoffmann
558 b8e59f18 malc
    obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
559 b8e59f18 malc
560 ea9ebc2c Marc-André Lureau
    pa->stream = qpa_simple_new (
561 ea9ebc2c Marc-André Lureau
        glob_paaudio.server,
562 b8e59f18 malc
        "qemu",
563 b8e59f18 malc
        PA_STREAM_PLAYBACK,
564 ea9ebc2c Marc-André Lureau
        glob_paaudio.sink,
565 b8e59f18 malc
        "pcm.playback",
566 b8e59f18 malc
        &ss,
567 b8e59f18 malc
        NULL,                   /* channel map */
568 e6d16fa4 Gerd Hoffmann
        &ba,                    /* buffering attributes */
569 b8e59f18 malc
        &error
570 b8e59f18 malc
        );
571 ea9ebc2c Marc-André Lureau
    if (!pa->stream) {
572 b8e59f18 malc
        qpa_logerr (error, "pa_simple_new for playback failed\n");
573 b8e59f18 malc
        goto fail1;
574 b8e59f18 malc
    }
575 b8e59f18 malc
576 b8e59f18 malc
    audio_pcm_init_info (&hw->info, &obt_as);
577 ea9ebc2c Marc-André Lureau
    hw->samples = glob_paaudio.samples;
578 b8e59f18 malc
    pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
579 6315633b Gerd Hoffmann
    pa->rpos = hw->rpos;
580 b8e59f18 malc
    if (!pa->pcm_buf) {
581 b8e59f18 malc
        dolog ("Could not allocate buffer (%d bytes)\n",
582 b8e59f18 malc
               hw->samples << hw->info.shift);
583 b8e59f18 malc
        goto fail2;
584 b8e59f18 malc
    }
585 b8e59f18 malc
586 b8e59f18 malc
    if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
587 b8e59f18 malc
        goto fail3;
588 b8e59f18 malc
    }
589 b8e59f18 malc
590 b8e59f18 malc
    return 0;
591 b8e59f18 malc
592 b8e59f18 malc
 fail3:
593 7267c094 Anthony Liguori
    g_free (pa->pcm_buf);
594 b8e59f18 malc
    pa->pcm_buf = NULL;
595 b8e59f18 malc
 fail2:
596 ea9ebc2c Marc-André Lureau
    if (pa->stream) {
597 ea9ebc2c Marc-André Lureau
        pa_stream_unref (pa->stream);
598 ea9ebc2c Marc-André Lureau
        pa->stream = NULL;
599 ea9ebc2c Marc-André Lureau
    }
600 b8e59f18 malc
 fail1:
601 b8e59f18 malc
    return -1;
602 b8e59f18 malc
}
603 b8e59f18 malc
604 1ea879e5 malc
static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
605 b8e59f18 malc
{
606 b8e59f18 malc
    int error;
607 b8e59f18 malc
    static pa_sample_spec ss;
608 1ea879e5 malc
    struct audsettings obt_as = *as;
609 b8e59f18 malc
    PAVoiceIn *pa = (PAVoiceIn *) hw;
610 b8e59f18 malc
611 b8e59f18 malc
    ss.format = audfmt_to_pa (as->fmt, as->endianness);
612 b8e59f18 malc
    ss.channels = as->nchannels;
613 b8e59f18 malc
    ss.rate = as->freq;
614 b8e59f18 malc
615 b8e59f18 malc
    obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
616 b8e59f18 malc
617 ea9ebc2c Marc-André Lureau
    pa->stream = qpa_simple_new (
618 ea9ebc2c Marc-André Lureau
        glob_paaudio.server,
619 b8e59f18 malc
        "qemu",
620 b8e59f18 malc
        PA_STREAM_RECORD,
621 ea9ebc2c Marc-André Lureau
        glob_paaudio.source,
622 b8e59f18 malc
        "pcm.capture",
623 b8e59f18 malc
        &ss,
624 b8e59f18 malc
        NULL,                   /* channel map */
625 b8e59f18 malc
        NULL,                   /* buffering attributes */
626 b8e59f18 malc
        &error
627 b8e59f18 malc
        );
628 ea9ebc2c Marc-André Lureau
    if (!pa->stream) {
629 b8e59f18 malc
        qpa_logerr (error, "pa_simple_new for capture failed\n");
630 b8e59f18 malc
        goto fail1;
631 b8e59f18 malc
    }
632 b8e59f18 malc
633 b8e59f18 malc
    audio_pcm_init_info (&hw->info, &obt_as);
634 ea9ebc2c Marc-André Lureau
    hw->samples = glob_paaudio.samples;
635 b8e59f18 malc
    pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
636 6315633b Gerd Hoffmann
    pa->wpos = hw->wpos;
637 b8e59f18 malc
    if (!pa->pcm_buf) {
638 b8e59f18 malc
        dolog ("Could not allocate buffer (%d bytes)\n",
639 b8e59f18 malc
               hw->samples << hw->info.shift);
640 b8e59f18 malc
        goto fail2;
641 b8e59f18 malc
    }
642 b8e59f18 malc
643 b8e59f18 malc
    if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
644 b8e59f18 malc
        goto fail3;
645 b8e59f18 malc
    }
646 b8e59f18 malc
647 b8e59f18 malc
    return 0;
648 b8e59f18 malc
649 b8e59f18 malc
 fail3:
650 7267c094 Anthony Liguori
    g_free (pa->pcm_buf);
651 b8e59f18 malc
    pa->pcm_buf = NULL;
652 b8e59f18 malc
 fail2:
653 ea9ebc2c Marc-André Lureau
    if (pa->stream) {
654 ea9ebc2c Marc-André Lureau
        pa_stream_unref (pa->stream);
655 ea9ebc2c Marc-André Lureau
        pa->stream = NULL;
656 ea9ebc2c Marc-André Lureau
    }
657 b8e59f18 malc
 fail1:
658 b8e59f18 malc
    return -1;
659 b8e59f18 malc
}
660 b8e59f18 malc
661 b8e59f18 malc
static void qpa_fini_out (HWVoiceOut *hw)
662 b8e59f18 malc
{
663 b8e59f18 malc
    void *ret;
664 b8e59f18 malc
    PAVoiceOut *pa = (PAVoiceOut *) hw;
665 b8e59f18 malc
666 b8e59f18 malc
    audio_pt_lock (&pa->pt, AUDIO_FUNC);
667 b8e59f18 malc
    pa->done = 1;
668 b8e59f18 malc
    audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
669 b8e59f18 malc
    audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
670 b8e59f18 malc
671 ea9ebc2c Marc-André Lureau
    if (pa->stream) {
672 ea9ebc2c Marc-André Lureau
        pa_stream_unref (pa->stream);
673 ea9ebc2c Marc-André Lureau
        pa->stream = NULL;
674 b8e59f18 malc
    }
675 b8e59f18 malc
676 b8e59f18 malc
    audio_pt_fini (&pa->pt, AUDIO_FUNC);
677 7267c094 Anthony Liguori
    g_free (pa->pcm_buf);
678 b8e59f18 malc
    pa->pcm_buf = NULL;
679 b8e59f18 malc
}
680 b8e59f18 malc
681 b8e59f18 malc
static void qpa_fini_in (HWVoiceIn *hw)
682 b8e59f18 malc
{
683 b8e59f18 malc
    void *ret;
684 b8e59f18 malc
    PAVoiceIn *pa = (PAVoiceIn *) hw;
685 b8e59f18 malc
686 b8e59f18 malc
    audio_pt_lock (&pa->pt, AUDIO_FUNC);
687 b8e59f18 malc
    pa->done = 1;
688 b8e59f18 malc
    audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
689 b8e59f18 malc
    audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
690 b8e59f18 malc
691 ea9ebc2c Marc-André Lureau
    if (pa->stream) {
692 ea9ebc2c Marc-André Lureau
        pa_stream_unref (pa->stream);
693 ea9ebc2c Marc-André Lureau
        pa->stream = NULL;
694 b8e59f18 malc
    }
695 b8e59f18 malc
696 b8e59f18 malc
    audio_pt_fini (&pa->pt, AUDIO_FUNC);
697 7267c094 Anthony Liguori
    g_free (pa->pcm_buf);
698 b8e59f18 malc
    pa->pcm_buf = NULL;
699 b8e59f18 malc
}
700 b8e59f18 malc
701 b8e59f18 malc
static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
702 b8e59f18 malc
{
703 6e7a7f3d Marc-André Lureau
    PAVoiceOut *pa = (PAVoiceOut *) hw;
704 6e7a7f3d Marc-André Lureau
    pa_operation *op;
705 6e7a7f3d Marc-André Lureau
    pa_cvolume v;
706 6e7a7f3d Marc-André Lureau
    paaudio *g = &glob_paaudio;
707 6e7a7f3d Marc-André Lureau
708 8f473dd1 Gerd Hoffmann
#ifdef PA_CHECK_VERSION    /* macro is present in 0.9.16+ */
709 8f473dd1 Gerd Hoffmann
    pa_cvolume_init (&v);  /* function is present in 0.9.13+ */
710 8f473dd1 Gerd Hoffmann
#endif
711 6e7a7f3d Marc-André Lureau
712 6e7a7f3d Marc-André Lureau
    switch (cmd) {
713 6e7a7f3d Marc-André Lureau
    case VOICE_VOLUME:
714 6e7a7f3d Marc-André Lureau
        {
715 6e7a7f3d Marc-André Lureau
            SWVoiceOut *sw;
716 6e7a7f3d Marc-André Lureau
            va_list ap;
717 6e7a7f3d Marc-André Lureau
718 6e7a7f3d Marc-André Lureau
            va_start (ap, cmd);
719 6e7a7f3d Marc-André Lureau
            sw = va_arg (ap, SWVoiceOut *);
720 6e7a7f3d Marc-André Lureau
            va_end (ap);
721 6e7a7f3d Marc-André Lureau
722 6e7a7f3d Marc-André Lureau
            v.channels = 2;
723 6e7a7f3d Marc-André Lureau
            v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
724 6e7a7f3d Marc-André Lureau
            v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
725 6e7a7f3d Marc-André Lureau
726 6e7a7f3d Marc-André Lureau
            pa_threaded_mainloop_lock (g->mainloop);
727 6e7a7f3d Marc-André Lureau
728 6e7a7f3d Marc-André Lureau
            op = pa_context_set_sink_input_volume (g->context,
729 6e7a7f3d Marc-André Lureau
                pa_stream_get_index (pa->stream),
730 6e7a7f3d Marc-André Lureau
                &v, NULL, NULL);
731 6e7a7f3d Marc-André Lureau
            if (!op)
732 6e7a7f3d Marc-André Lureau
                qpa_logerr (pa_context_errno (g->context),
733 6e7a7f3d Marc-André Lureau
                            "set_sink_input_volume() failed\n");
734 6e7a7f3d Marc-André Lureau
            else
735 6e7a7f3d Marc-André Lureau
                pa_operation_unref (op);
736 6e7a7f3d Marc-André Lureau
737 6e7a7f3d Marc-André Lureau
            op = pa_context_set_sink_input_mute (g->context,
738 6e7a7f3d Marc-André Lureau
                pa_stream_get_index (pa->stream),
739 6e7a7f3d Marc-André Lureau
               sw->vol.mute, NULL, NULL);
740 6e7a7f3d Marc-André Lureau
            if (!op) {
741 6e7a7f3d Marc-André Lureau
                qpa_logerr (pa_context_errno (g->context),
742 6e7a7f3d Marc-André Lureau
                            "set_sink_input_mute() failed\n");
743 6e7a7f3d Marc-André Lureau
            } else {
744 6e7a7f3d Marc-André Lureau
                pa_operation_unref (op);
745 6e7a7f3d Marc-André Lureau
            }
746 6e7a7f3d Marc-André Lureau
747 6e7a7f3d Marc-André Lureau
            pa_threaded_mainloop_unlock (g->mainloop);
748 6e7a7f3d Marc-André Lureau
        }
749 6e7a7f3d Marc-André Lureau
    }
750 b8e59f18 malc
    return 0;
751 b8e59f18 malc
}
752 b8e59f18 malc
753 b8e59f18 malc
static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
754 b8e59f18 malc
{
755 6e7a7f3d Marc-André Lureau
    PAVoiceIn *pa = (PAVoiceIn *) hw;
756 6e7a7f3d Marc-André Lureau
    pa_operation *op;
757 6e7a7f3d Marc-André Lureau
    pa_cvolume v;
758 6e7a7f3d Marc-André Lureau
    paaudio *g = &glob_paaudio;
759 6e7a7f3d Marc-André Lureau
760 8f473dd1 Gerd Hoffmann
#ifdef PA_CHECK_VERSION
761 6e7a7f3d Marc-André Lureau
    pa_cvolume_init (&v);
762 8f473dd1 Gerd Hoffmann
#endif
763 6e7a7f3d Marc-André Lureau
764 6e7a7f3d Marc-André Lureau
    switch (cmd) {
765 6e7a7f3d Marc-André Lureau
    case VOICE_VOLUME:
766 6e7a7f3d Marc-André Lureau
        {
767 6e7a7f3d Marc-André Lureau
            SWVoiceIn *sw;
768 6e7a7f3d Marc-André Lureau
            va_list ap;
769 6e7a7f3d Marc-André Lureau
770 6e7a7f3d Marc-André Lureau
            va_start (ap, cmd);
771 6e7a7f3d Marc-André Lureau
            sw = va_arg (ap, SWVoiceIn *);
772 6e7a7f3d Marc-André Lureau
            va_end (ap);
773 6e7a7f3d Marc-André Lureau
774 6e7a7f3d Marc-André Lureau
            v.channels = 2;
775 6e7a7f3d Marc-André Lureau
            v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
776 6e7a7f3d Marc-André Lureau
            v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
777 6e7a7f3d Marc-André Lureau
778 6e7a7f3d Marc-André Lureau
            pa_threaded_mainloop_lock (g->mainloop);
779 6e7a7f3d Marc-André Lureau
780 6e7a7f3d Marc-André Lureau
            /* FIXME: use the upcoming "set_source_output_{volume,mute}" */
781 6e7a7f3d Marc-André Lureau
            op = pa_context_set_source_volume_by_index (g->context,
782 6e7a7f3d Marc-André Lureau
                pa_stream_get_device_index (pa->stream),
783 6e7a7f3d Marc-André Lureau
                &v, NULL, NULL);
784 6e7a7f3d Marc-André Lureau
            if (!op) {
785 6e7a7f3d Marc-André Lureau
                qpa_logerr (pa_context_errno (g->context),
786 6e7a7f3d Marc-André Lureau
                            "set_source_volume() failed\n");
787 6e7a7f3d Marc-André Lureau
            } else {
788 6e7a7f3d Marc-André Lureau
                pa_operation_unref(op);
789 6e7a7f3d Marc-André Lureau
            }
790 6e7a7f3d Marc-André Lureau
791 6e7a7f3d Marc-André Lureau
            op = pa_context_set_source_mute_by_index (g->context,
792 6e7a7f3d Marc-André Lureau
                pa_stream_get_index (pa->stream),
793 6e7a7f3d Marc-André Lureau
                sw->vol.mute, NULL, NULL);
794 6e7a7f3d Marc-André Lureau
            if (!op) {
795 6e7a7f3d Marc-André Lureau
                qpa_logerr (pa_context_errno (g->context),
796 6e7a7f3d Marc-André Lureau
                            "set_source_mute() failed\n");
797 6e7a7f3d Marc-André Lureau
            } else {
798 6e7a7f3d Marc-André Lureau
                pa_operation_unref (op);
799 6e7a7f3d Marc-André Lureau
            }
800 6e7a7f3d Marc-André Lureau
801 6e7a7f3d Marc-André Lureau
            pa_threaded_mainloop_unlock (g->mainloop);
802 6e7a7f3d Marc-André Lureau
        }
803 6e7a7f3d Marc-André Lureau
    }
804 b8e59f18 malc
    return 0;
805 b8e59f18 malc
}
806 b8e59f18 malc
807 b8e59f18 malc
/* common */
808 b8e59f18 malc
static void *qpa_audio_init (void)
809 b8e59f18 malc
{
810 ea9ebc2c Marc-André Lureau
    paaudio *g = &glob_paaudio;
811 ea9ebc2c Marc-André Lureau
812 ea9ebc2c Marc-André Lureau
    g->mainloop = pa_threaded_mainloop_new ();
813 ea9ebc2c Marc-André Lureau
    if (!g->mainloop) {
814 ea9ebc2c Marc-André Lureau
        goto fail;
815 ea9ebc2c Marc-André Lureau
    }
816 ea9ebc2c Marc-André Lureau
817 ea9ebc2c Marc-André Lureau
    g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server);
818 ea9ebc2c Marc-André Lureau
    if (!g->context) {
819 ea9ebc2c Marc-André Lureau
        goto fail;
820 ea9ebc2c Marc-André Lureau
    }
821 ea9ebc2c Marc-André Lureau
822 ea9ebc2c Marc-André Lureau
    pa_context_set_state_callback (g->context, context_state_cb, g);
823 ea9ebc2c Marc-André Lureau
824 ea9ebc2c Marc-André Lureau
    if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) {
825 ea9ebc2c Marc-André Lureau
        qpa_logerr (pa_context_errno (g->context),
826 ea9ebc2c Marc-André Lureau
                    "pa_context_connect() failed\n");
827 ea9ebc2c Marc-André Lureau
        goto fail;
828 ea9ebc2c Marc-André Lureau
    }
829 ea9ebc2c Marc-André Lureau
830 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop_lock (g->mainloop);
831 ea9ebc2c Marc-André Lureau
832 ea9ebc2c Marc-André Lureau
    if (pa_threaded_mainloop_start (g->mainloop) < 0) {
833 ea9ebc2c Marc-André Lureau
        goto unlock_and_fail;
834 ea9ebc2c Marc-André Lureau
    }
835 ea9ebc2c Marc-André Lureau
836 ea9ebc2c Marc-André Lureau
    for (;;) {
837 ea9ebc2c Marc-André Lureau
        pa_context_state_t state;
838 ea9ebc2c Marc-André Lureau
839 ea9ebc2c Marc-André Lureau
        state = pa_context_get_state (g->context);
840 ea9ebc2c Marc-André Lureau
841 ea9ebc2c Marc-André Lureau
        if (state == PA_CONTEXT_READY) {
842 ea9ebc2c Marc-André Lureau
            break;
843 ea9ebc2c Marc-André Lureau
        }
844 ea9ebc2c Marc-André Lureau
845 ea9ebc2c Marc-André Lureau
        if (!PA_CONTEXT_IS_GOOD (state)) {
846 ea9ebc2c Marc-André Lureau
            qpa_logerr (pa_context_errno (g->context),
847 ea9ebc2c Marc-André Lureau
                        "Wrong context state\n");
848 ea9ebc2c Marc-André Lureau
            goto unlock_and_fail;
849 ea9ebc2c Marc-André Lureau
        }
850 ea9ebc2c Marc-André Lureau
851 ea9ebc2c Marc-André Lureau
        /* Wait until the context is ready */
852 ea9ebc2c Marc-André Lureau
        pa_threaded_mainloop_wait (g->mainloop);
853 ea9ebc2c Marc-André Lureau
    }
854 ea9ebc2c Marc-André Lureau
855 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop_unlock (g->mainloop);
856 ea9ebc2c Marc-André Lureau
857 ea9ebc2c Marc-André Lureau
    return &glob_paaudio;
858 ea9ebc2c Marc-André Lureau
859 ea9ebc2c Marc-André Lureau
unlock_and_fail:
860 ea9ebc2c Marc-André Lureau
    pa_threaded_mainloop_unlock (g->mainloop);
861 ea9ebc2c Marc-André Lureau
fail:
862 ea9ebc2c Marc-André Lureau
    AUD_log (AUDIO_CAP, "Failed to initialize PA context");
863 ea9ebc2c Marc-André Lureau
    return NULL;
864 b8e59f18 malc
}
865 b8e59f18 malc
866 b8e59f18 malc
static void qpa_audio_fini (void *opaque)
867 b8e59f18 malc
{
868 ea9ebc2c Marc-André Lureau
    paaudio *g = opaque;
869 ea9ebc2c Marc-André Lureau
870 ea9ebc2c Marc-André Lureau
    if (g->mainloop) {
871 ea9ebc2c Marc-André Lureau
        pa_threaded_mainloop_stop (g->mainloop);
872 ea9ebc2c Marc-André Lureau
    }
873 ea9ebc2c Marc-André Lureau
874 ea9ebc2c Marc-André Lureau
    if (g->context) {
875 ea9ebc2c Marc-André Lureau
        pa_context_disconnect (g->context);
876 ea9ebc2c Marc-André Lureau
        pa_context_unref (g->context);
877 ea9ebc2c Marc-André Lureau
        g->context = NULL;
878 ea9ebc2c Marc-André Lureau
    }
879 ea9ebc2c Marc-André Lureau
880 ea9ebc2c Marc-André Lureau
    if (g->mainloop) {
881 ea9ebc2c Marc-André Lureau
        pa_threaded_mainloop_free (g->mainloop);
882 ea9ebc2c Marc-André Lureau
    }
883 ea9ebc2c Marc-André Lureau
884 ea9ebc2c Marc-André Lureau
    g->mainloop = NULL;
885 b8e59f18 malc
}
886 b8e59f18 malc
887 b8e59f18 malc
struct audio_option qpa_options[] = {
888 98f9f48c malc
    {
889 98f9f48c malc
        .name  = "SAMPLES",
890 98f9f48c malc
        .tag   = AUD_OPT_INT,
891 ea9ebc2c Marc-André Lureau
        .valp  = &glob_paaudio.samples,
892 98f9f48c malc
        .descr = "buffer size in samples"
893 98f9f48c malc
    },
894 98f9f48c malc
    {
895 98f9f48c malc
        .name  = "SERVER",
896 98f9f48c malc
        .tag   = AUD_OPT_STR,
897 ea9ebc2c Marc-André Lureau
        .valp  = &glob_paaudio.server,
898 98f9f48c malc
        .descr = "server address"
899 98f9f48c malc
    },
900 98f9f48c malc
    {
901 98f9f48c malc
        .name  = "SINK",
902 98f9f48c malc
        .tag   = AUD_OPT_STR,
903 ea9ebc2c Marc-André Lureau
        .valp  = &glob_paaudio.sink,
904 98f9f48c malc
        .descr = "sink device name"
905 98f9f48c malc
    },
906 98f9f48c malc
    {
907 98f9f48c malc
        .name  = "SOURCE",
908 98f9f48c malc
        .tag   = AUD_OPT_STR,
909 ea9ebc2c Marc-André Lureau
        .valp  = &glob_paaudio.source,
910 98f9f48c malc
        .descr = "source device name"
911 98f9f48c malc
    },
912 2700efa3 Juan Quintela
    { /* End of list */ }
913 b8e59f18 malc
};
914 b8e59f18 malc
915 35f4b58c blueswir1
static struct audio_pcm_ops qpa_pcm_ops = {
916 1dd3e4d1 Juan Quintela
    .init_out = qpa_init_out,
917 1dd3e4d1 Juan Quintela
    .fini_out = qpa_fini_out,
918 1dd3e4d1 Juan Quintela
    .run_out  = qpa_run_out,
919 1dd3e4d1 Juan Quintela
    .write    = qpa_write,
920 1dd3e4d1 Juan Quintela
    .ctl_out  = qpa_ctl_out,
921 1dd3e4d1 Juan Quintela
922 1dd3e4d1 Juan Quintela
    .init_in  = qpa_init_in,
923 1dd3e4d1 Juan Quintela
    .fini_in  = qpa_fini_in,
924 1dd3e4d1 Juan Quintela
    .run_in   = qpa_run_in,
925 1dd3e4d1 Juan Quintela
    .read     = qpa_read,
926 1dd3e4d1 Juan Quintela
    .ctl_in   = qpa_ctl_in
927 b8e59f18 malc
};
928 b8e59f18 malc
929 b8e59f18 malc
struct audio_driver pa_audio_driver = {
930 bee37f32 Juan Quintela
    .name           = "pa",
931 bee37f32 Juan Quintela
    .descr          = "http://www.pulseaudio.org/",
932 bee37f32 Juan Quintela
    .options        = qpa_options,
933 bee37f32 Juan Quintela
    .init           = qpa_audio_init,
934 bee37f32 Juan Quintela
    .fini           = qpa_audio_fini,
935 bee37f32 Juan Quintela
    .pcm_ops        = &qpa_pcm_ops,
936 1a4ea1e3 Michael S. Tsirkin
    .can_be_default = 1,
937 bee37f32 Juan Quintela
    .max_voices_out = INT_MAX,
938 bee37f32 Juan Quintela
    .max_voices_in  = INT_MAX,
939 bee37f32 Juan Quintela
    .voice_size_out = sizeof (PAVoiceOut),
940 6e7a7f3d Marc-André Lureau
    .voice_size_in  = sizeof (PAVoiceIn),
941 6e7a7f3d Marc-André Lureau
    .ctl_caps       = VOICE_VOLUME_CAP
942 b8e59f18 malc
};