blob: 75401d53910a0d55c80c3a34507ddc2469f0bd11 [file] [log] [blame]
malcb8e59f12008-07-02 21:03:08 +00001/* public domain */
Markus Armbruster0b8fa322019-05-23 16:35:07 +02002
Peter Maydell6086a562016-01-18 17:33:52 +00003#include "qemu/osdep.h"
Markus Armbruster0b8fa322019-05-23 16:35:07 +02004#include "qemu/module.h"
malcb8e59f12008-07-02 21:03:08 +00005#include "audio.h"
Kővágó, Zoltán2c324b22019-03-08 23:34:21 +01006#include "qapi/opts-visitor.h"
malcb8e59f12008-07-02 21:03:08 +00007
Marc-André Lureauea9ebc22012-04-17 14:32:41 +02008#include <pulse/pulseaudio.h>
malcb8e59f12008-07-02 21:03:08 +00009
10#define AUDIO_CAP "pulseaudio"
11#include "audio_int.h"
malcb8e59f12008-07-02 21:03:08 +000012
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +020013typedef struct PAConnection {
14 char *server;
15 int refcount;
16 QTAILQ_ENTRY(PAConnection) list;
17
Kővágó, Zoltán9a644c42015-06-03 23:03:49 +020018 pa_threaded_mainloop *mainloop;
19 pa_context *context;
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +020020} PAConnection;
21
22static QTAILQ_HEAD(PAConnectionHead, PAConnection) pa_conns =
23 QTAILQ_HEAD_INITIALIZER(pa_conns);
24
25typedef struct {
26 Audiodev *dev;
27 PAConnection *conn;
Kővágó, Zoltán9a644c42015-06-03 23:03:49 +020028} paaudio;
29
30typedef struct {
malcb8e59f12008-07-02 21:03:08 +000031 HWVoiceOut hw;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +020032 pa_stream *stream;
Kővágó, Zoltán9a644c42015-06-03 23:03:49 +020033 paaudio *g;
malcb8e59f12008-07-02 21:03:08 +000034} PAVoiceOut;
35
36typedef struct {
37 HWVoiceIn hw;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +020038 pa_stream *stream;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +020039 const void *read_data;
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +020040 size_t read_length;
Kővágó, Zoltán9a644c42015-06-03 23:03:49 +020041 paaudio *g;
malcb8e59f12008-07-02 21:03:08 +000042} PAVoiceIn;
43
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +020044static void qpa_conn_fini(PAConnection *c);
Kővágó, Zoltán49dd6d02015-06-03 23:03:53 +020045
malcb8e59f12008-07-02 21:03:08 +000046static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
47{
48 va_list ap;
49
50 va_start (ap, fmt);
51 AUD_vlog (AUDIO_CAP, fmt, ap);
52 va_end (ap);
53
54 AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
55}
56
Gerd Hoffmann8f473dd2012-05-03 22:41:28 +020057#ifndef PA_CONTEXT_IS_GOOD
58static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
59{
60 return
61 x == PA_CONTEXT_CONNECTING ||
62 x == PA_CONTEXT_AUTHORIZING ||
63 x == PA_CONTEXT_SETTING_NAME ||
64 x == PA_CONTEXT_READY;
65}
66#endif
67
68#ifndef PA_STREAM_IS_GOOD
69static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
70{
71 return
72 x == PA_STREAM_CREATING ||
73 x == PA_STREAM_READY;
74}
75#endif
76
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +020077#define CHECK_SUCCESS_GOTO(c, expression, label, msg) \
Marc-André Lureauea9ebc22012-04-17 14:32:41 +020078 do { \
79 if (!(expression)) { \
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +020080 qpa_logerr(pa_context_errno((c)->context), msg); \
Marc-André Lureauea9ebc22012-04-17 14:32:41 +020081 goto label; \
82 } \
Eric Blake25627552017-12-01 17:24:32 -060083 } while (0)
Marc-André Lureauea9ebc22012-04-17 14:32:41 +020084
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +020085#define CHECK_DEAD_GOTO(c, stream, label, msg) \
Marc-André Lureauea9ebc22012-04-17 14:32:41 +020086 do { \
87 if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
88 !(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
89 if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
90 ((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +020091 qpa_logerr(pa_context_errno((c)->context), msg); \
Marc-André Lureauea9ebc22012-04-17 14:32:41 +020092 } else { \
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +020093 qpa_logerr(PA_ERR_BADSTATE, msg); \
Marc-André Lureauea9ebc22012-04-17 14:32:41 +020094 } \
95 goto label; \
96 } \
Eric Blake25627552017-12-01 17:24:32 -060097 } while (0)
Marc-André Lureauea9ebc22012-04-17 14:32:41 +020098
Kővágó, Zoltán337e8de2019-10-13 21:58:00 +020099static void *qpa_get_buffer_in(HWVoiceIn *hw, size_t *size)
100{
101 PAVoiceIn *p = (PAVoiceIn *) hw;
102 PAConnection *c = p->g->conn;
103 int r;
104
105 pa_threaded_mainloop_lock(c->mainloop);
106
107 CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
108 "pa_threaded_mainloop_lock failed\n");
109
110 if (!p->read_length) {
111 r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
112 CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
113 "pa_stream_peek failed\n");
114 }
115
116 *size = MIN(p->read_length, *size);
117
118 pa_threaded_mainloop_unlock(c->mainloop);
119 return (void *) p->read_data;
120
121unlock_and_fail:
122 pa_threaded_mainloop_unlock(c->mainloop);
123 *size = 0;
124 return NULL;
125}
126
127static void qpa_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
128{
129 PAVoiceIn *p = (PAVoiceIn *) hw;
130 PAConnection *c = p->g->conn;
131 int r;
132
133 pa_threaded_mainloop_lock(c->mainloop);
134
135 CHECK_DEAD_GOTO(c, p->stream, unlock,
136 "pa_threaded_mainloop_lock failed\n");
137
138 assert(buf == p->read_data && size <= p->read_length);
139
140 p->read_data += size;
141 p->read_length -= size;
142
143 if (size && !p->read_length) {
144 r = pa_stream_drop(p->stream);
145 CHECK_SUCCESS_GOTO(c, r == 0, unlock, "pa_stream_drop failed\n");
146 }
147
148unlock:
149 pa_threaded_mainloop_unlock(c->mainloop);
150}
151
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200152static size_t qpa_read(HWVoiceIn *hw, void *data, size_t length)
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200153{
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200154 PAVoiceIn *p = (PAVoiceIn *) hw;
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200155 PAConnection *c = p->g->conn;
Volker Rümelinacc3b632020-01-04 10:11:21 +0100156 size_t total = 0;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200157
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200158 pa_threaded_mainloop_lock(c->mainloop);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200159
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200160 CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
161 "pa_threaded_mainloop_lock failed\n");
Volker Rümelin7c9eb862020-01-04 10:11:22 +0100162 if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
163 /* wait for stream to become ready */
164 goto unlock;
165 }
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200166
Volker Rümelinacc3b632020-01-04 10:11:21 +0100167 while (total < length) {
168 size_t l;
169 int r;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200170
Volker Rümelinacc3b632020-01-04 10:11:21 +0100171 if (!p->read_length) {
172 r = pa_stream_peek(p->stream, &p->read_data, &p->read_length);
173 CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
174 "pa_stream_peek failed\n");
175 if (!p->read_length) {
176 /* buffer is empty */
177 break;
178 }
179 }
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200180
Volker Rümelinacc3b632020-01-04 10:11:21 +0100181 l = MIN(p->read_length, length - total);
182 memcpy((char *)data + total, p->read_data, l);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200183
Volker Rümelinacc3b632020-01-04 10:11:21 +0100184 p->read_data += l;
185 p->read_length -= l;
186 total += l;
187
188 if (!p->read_length) {
189 r = pa_stream_drop(p->stream);
190 CHECK_SUCCESS_GOTO(c, r == 0, unlock_and_fail,
191 "pa_stream_drop failed\n");
192 }
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200193 }
194
Volker Rümelin7c9eb862020-01-04 10:11:22 +0100195unlock:
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200196 pa_threaded_mainloop_unlock(c->mainloop);
Volker Rümelinacc3b632020-01-04 10:11:21 +0100197 return total;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200198
199unlock_and_fail:
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200200 pa_threaded_mainloop_unlock(c->mainloop);
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200201 return 0;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200202}
203
Kővágó, Zoltán337e8de2019-10-13 21:58:00 +0200204static void *qpa_get_buffer_out(HWVoiceOut *hw, size_t *size)
205{
206 PAVoiceOut *p = (PAVoiceOut *) hw;
207 PAConnection *c = p->g->conn;
208 void *ret;
Volker Rümelinbea29e92021-01-10 11:02:29 +0100209 size_t l;
Kővágó, Zoltán337e8de2019-10-13 21:58:00 +0200210 int r;
211
212 pa_threaded_mainloop_lock(c->mainloop);
213
214 CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
215 "pa_threaded_mainloop_lock failed\n");
Volker Rümelin7007cd32021-01-10 11:02:31 +0100216 if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
217 /* wait for stream to become ready */
218 l = 0;
219 ret = NULL;
220 goto unlock;
221 }
Kővágó, Zoltán337e8de2019-10-13 21:58:00 +0200222
Volker Rümelinbea29e92021-01-10 11:02:29 +0100223 l = pa_stream_writable_size(p->stream);
224 CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
225 "pa_stream_writable_size failed\n");
226
Kővágó, Zoltán337e8de2019-10-13 21:58:00 +0200227 *size = -1;
228 r = pa_stream_begin_write(p->stream, &ret, size);
229 CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail,
230 "pa_stream_begin_write failed\n");
231
Volker Rümelin7007cd32021-01-10 11:02:31 +0100232unlock:
Kővágó, Zoltán337e8de2019-10-13 21:58:00 +0200233 pa_threaded_mainloop_unlock(c->mainloop);
Volker Rümelinbea29e92021-01-10 11:02:29 +0100234 if (*size > l) {
235 *size = l;
236 }
Kővágó, Zoltán337e8de2019-10-13 21:58:00 +0200237 return ret;
238
239unlock_and_fail:
240 pa_threaded_mainloop_unlock(c->mainloop);
241 *size = 0;
242 return NULL;
243}
244
Volker Rümelinbea29e92021-01-10 11:02:29 +0100245static size_t qpa_put_buffer_out(HWVoiceOut *hw, void *data, size_t length)
246{
247 PAVoiceOut *p = (PAVoiceOut *)hw;
248 PAConnection *c = p->g->conn;
249 int r;
250
251 pa_threaded_mainloop_lock(c->mainloop);
252
253 CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
254 "pa_threaded_mainloop_lock failed\n");
255
256 r = pa_stream_write(p->stream, data, length, NULL, 0LL, PA_SEEK_RELATIVE);
257 CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_write failed\n");
258
259 pa_threaded_mainloop_unlock(c->mainloop);
260 return length;
261
262unlock_and_fail:
263 pa_threaded_mainloop_unlock(c->mainloop);
264 return 0;
265}
266
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200267static size_t qpa_write(HWVoiceOut *hw, void *data, size_t length)
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200268{
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200269 PAVoiceOut *p = (PAVoiceOut *) hw;
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200270 PAConnection *c = p->g->conn;
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200271 size_t l;
272 int r;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200273
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200274 pa_threaded_mainloop_lock(c->mainloop);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200275
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200276 CHECK_DEAD_GOTO(c, p->stream, unlock_and_fail,
277 "pa_threaded_mainloop_lock failed\n");
Volker Rümeline270c542021-01-10 11:02:30 +0100278 if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
279 /* wait for stream to become ready */
280 l = 0;
281 goto unlock;
282 }
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200283
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200284 l = pa_stream_writable_size(p->stream);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200285
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200286 CHECK_SUCCESS_GOTO(c, l != (size_t) -1, unlock_and_fail,
287 "pa_stream_writable_size failed\n");
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200288
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200289 if (l > length) {
290 l = length;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200291 }
292
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200293 r = pa_stream_write(p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
294 CHECK_SUCCESS_GOTO(c, r >= 0, unlock_and_fail, "pa_stream_write failed\n");
295
Volker Rümeline270c542021-01-10 11:02:30 +0100296unlock:
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200297 pa_threaded_mainloop_unlock(c->mainloop);
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200298 return l;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200299
300unlock_and_fail:
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200301 pa_threaded_mainloop_unlock(c->mainloop);
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200302 return 0;
malcb8e59f12008-07-02 21:03:08 +0000303}
304
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +0100305static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness)
malcb8e59f12008-07-02 21:03:08 +0000306{
307 int format;
308
309 switch (afmt) {
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +0100310 case AUDIO_FORMAT_S8:
311 case AUDIO_FORMAT_U8:
malcb8e59f12008-07-02 21:03:08 +0000312 format = PA_SAMPLE_U8;
313 break;
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +0100314 case AUDIO_FORMAT_S16:
315 case AUDIO_FORMAT_U16:
malcb8e59f12008-07-02 21:03:08 +0000316 format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE;
317 break;
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +0100318 case AUDIO_FORMAT_S32:
319 case AUDIO_FORMAT_U32:
malcb8e59f12008-07-02 21:03:08 +0000320 format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE;
321 break;
Kővágó, Zoltáned2a4a72020-02-02 20:38:07 +0100322 case AUDIO_FORMAT_F32:
323 format = endianness ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE;
324 break;
malcb8e59f12008-07-02 21:03:08 +0000325 default:
326 dolog ("Internal logic error: Bad audio format %d\n", afmt);
327 format = PA_SAMPLE_U8;
328 break;
329 }
330 return format;
331}
332
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +0100333static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
malcb8e59f12008-07-02 21:03:08 +0000334{
335 switch (fmt) {
336 case PA_SAMPLE_U8:
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +0100337 return AUDIO_FORMAT_U8;
malcb8e59f12008-07-02 21:03:08 +0000338 case PA_SAMPLE_S16BE:
339 *endianness = 1;
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +0100340 return AUDIO_FORMAT_S16;
malcb8e59f12008-07-02 21:03:08 +0000341 case PA_SAMPLE_S16LE:
342 *endianness = 0;
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +0100343 return AUDIO_FORMAT_S16;
malcb8e59f12008-07-02 21:03:08 +0000344 case PA_SAMPLE_S32BE:
345 *endianness = 1;
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +0100346 return AUDIO_FORMAT_S32;
malcb8e59f12008-07-02 21:03:08 +0000347 case PA_SAMPLE_S32LE:
348 *endianness = 0;
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +0100349 return AUDIO_FORMAT_S32;
Kővágó, Zoltáned2a4a72020-02-02 20:38:07 +0100350 case PA_SAMPLE_FLOAT32BE:
351 *endianness = 1;
352 return AUDIO_FORMAT_F32;
353 case PA_SAMPLE_FLOAT32LE:
354 *endianness = 0;
355 return AUDIO_FORMAT_F32;
malcb8e59f12008-07-02 21:03:08 +0000356 default:
357 dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
Kővágó, Zoltán85bc5852019-03-08 23:34:13 +0100358 return AUDIO_FORMAT_U8;
malcb8e59f12008-07-02 21:03:08 +0000359 }
360}
361
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200362static void context_state_cb (pa_context *c, void *userdata)
363{
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200364 PAConnection *conn = userdata;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200365
366 switch (pa_context_get_state(c)) {
367 case PA_CONTEXT_READY:
368 case PA_CONTEXT_TERMINATED:
369 case PA_CONTEXT_FAILED:
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200370 pa_threaded_mainloop_signal(conn->mainloop, 0);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200371 break;
372
373 case PA_CONTEXT_UNCONNECTED:
374 case PA_CONTEXT_CONNECTING:
375 case PA_CONTEXT_AUTHORIZING:
376 case PA_CONTEXT_SETTING_NAME:
377 break;
378 }
379}
380
381static void stream_state_cb (pa_stream *s, void * userdata)
382{
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200383 PAConnection *c = userdata;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200384
385 switch (pa_stream_get_state (s)) {
386
387 case PA_STREAM_READY:
388 case PA_STREAM_FAILED:
389 case PA_STREAM_TERMINATED:
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200390 pa_threaded_mainloop_signal(c->mainloop, 0);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200391 break;
392
393 case PA_STREAM_UNCONNECTED:
394 case PA_STREAM_CREATING:
395 break;
396 }
397}
398
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200399static pa_stream *qpa_simple_new (
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200400 PAConnection *c,
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200401 const char *name,
402 pa_stream_direction_t dir,
403 const char *dev,
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200404 const pa_sample_spec *ss,
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200405 const pa_buffer_attr *attr,
406 int *rerror)
407{
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200408 int r;
Kővágó, Zoltán0cf13e32019-10-13 21:58:07 +0200409 pa_stream *stream = NULL;
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200410 pa_stream_flags_t flags;
Kővágó, Zoltán0cf13e32019-10-13 21:58:07 +0200411 pa_channel_map map;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200412
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200413 pa_threaded_mainloop_lock(c->mainloop);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200414
Kővágó, Zoltán0cf13e32019-10-13 21:58:07 +0200415 pa_channel_map_init(&map);
416 map.channels = ss->channels;
417
418 /*
419 * TODO: This currently expects the only frontend supporting more than 2
420 * channels is the usb-audio. We will need some means to set channel
421 * order when a new frontend gains multi-channel support.
422 */
423 switch (ss->channels) {
424 case 1:
425 map.map[0] = PA_CHANNEL_POSITION_MONO;
426 break;
427
428 case 2:
429 map.map[0] = PA_CHANNEL_POSITION_LEFT;
430 map.map[1] = PA_CHANNEL_POSITION_RIGHT;
431 break;
432
433 case 6:
434 map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
435 map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
436 map.map[2] = PA_CHANNEL_POSITION_CENTER;
437 map.map[3] = PA_CHANNEL_POSITION_LFE;
438 map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
439 map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
440 break;
441
442 case 8:
443 map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
444 map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
445 map.map[2] = PA_CHANNEL_POSITION_CENTER;
446 map.map[3] = PA_CHANNEL_POSITION_LFE;
447 map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
448 map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
449 map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
450 map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
Paolo Bonzini56089562019-10-23 10:24:20 +0200451 break;
Kővágó, Zoltán0cf13e32019-10-13 21:58:07 +0200452
453 default:
454 dolog("Internal error: unsupported channel count %d\n", ss->channels);
455 goto fail;
456 }
457
458 stream = pa_stream_new(c->context, name, ss, &map);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200459 if (!stream) {
460 goto fail;
461 }
462
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200463 pa_stream_set_state_callback(stream, stream_state_cb, c);
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200464
Volker Rümelin50db82d2021-05-17 21:46:02 +0200465 flags = PA_STREAM_EARLY_REQUESTS;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200466
Kővágó, Zoltán8a435f72019-08-19 01:06:52 +0200467 if (dev) {
468 /* don't move the stream if the user specified a sink/source */
469 flags |= PA_STREAM_DONT_MOVE;
470 }
471
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200472 if (dir == PA_STREAM_PLAYBACK) {
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200473 r = pa_stream_connect_playback(stream, dev, attr, flags, NULL, NULL);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200474 } else {
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200475 r = pa_stream_connect_record(stream, dev, attr, flags);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200476 }
477
478 if (r < 0) {
Zhang Han289db3c2021-01-15 09:24:30 +0800479 goto fail;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200480 }
481
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200482 pa_threaded_mainloop_unlock(c->mainloop);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200483
484 return stream;
485
486fail:
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200487 pa_threaded_mainloop_unlock(c->mainloop);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200488
489 if (stream) {
490 pa_stream_unref (stream);
491 }
492
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200493 *rerror = pa_context_errno(c->context);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200494
495 return NULL;
496}
497
Kővágó, Zoltán5706db12015-06-03 23:03:47 +0200498static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
499 void *drv_opaque)
malcb8e59f12008-07-02 21:03:08 +0000500{
501 int error;
Kővágó, Zoltán9a644c42015-06-03 23:03:49 +0200502 pa_sample_spec ss;
503 pa_buffer_attr ba;
malc1ea879e2008-12-03 22:48:44 +0000504 struct audsettings obt_as = *as;
malcb8e59f12008-07-02 21:03:08 +0000505 PAVoiceOut *pa = (PAVoiceOut *) hw;
Kővágó, Zoltán9a644c42015-06-03 23:03:49 +0200506 paaudio *g = pa->g = drv_opaque;
Kővágó, Zoltán2c324b22019-03-08 23:34:21 +0100507 AudiodevPaOptions *popts = &g->dev->u.pa;
508 AudiodevPaPerDirectionOptions *ppdo = popts->out;
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200509 PAConnection *c = g->conn;
malcb8e59f12008-07-02 21:03:08 +0000510
511 ss.format = audfmt_to_pa (as->fmt, as->endianness);
512 ss.channels = as->nchannels;
513 ss.rate = as->freq;
514
Martin Schrodtf6142772019-03-15 09:46:52 +0100515 ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss);
Volker Rümelin00413ed2021-01-10 11:02:34 +0100516 ba.minreq = pa_usec_to_bytes(MIN(ppdo->latency >> 2,
517 (g->dev->timer_period >> 2) * 3), &ss);
Gerd Hoffmanne6d16fa2011-01-24 22:07:45 +0100518 ba.maxlength = -1;
519 ba.prebuf = -1;
520
malcb8e59f12008-07-02 21:03:08 +0000521 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
522
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200523 pa->stream = qpa_simple_new (
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200524 c,
Kővágó, Zoltánf47dffe82019-09-11 01:26:20 +0200525 ppdo->has_stream_name ? ppdo->stream_name : g->dev->id,
malcb8e59f12008-07-02 21:03:08 +0000526 PA_STREAM_PLAYBACK,
Kővágó, Zoltán2c324b22019-03-08 23:34:21 +0100527 ppdo->has_name ? ppdo->name : NULL,
malcb8e59f12008-07-02 21:03:08 +0000528 &ss,
Gerd Hoffmanne6d16fa2011-01-24 22:07:45 +0100529 &ba, /* buffering attributes */
malcb8e59f12008-07-02 21:03:08 +0000530 &error
531 );
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200532 if (!pa->stream) {
malcb8e59f12008-07-02 21:03:08 +0000533 qpa_logerr (error, "pa_simple_new for playback failed\n");
534 goto fail1;
535 }
536
537 audio_pcm_init_info (&hw->info, &obt_as);
Volker Rümelincffd2fd2021-01-10 11:02:33 +0100538 /*
539 * This is wrong. hw->samples counts in frames. hw->samples will be
540 * number of channels times larger than expected.
541 */
Volker Rümelina76e6b82020-01-23 08:49:37 +0100542 hw->samples = audio_buffer_samples(
Volker Rümelin521ce712021-01-10 11:02:32 +0100543 qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
malcb8e59f12008-07-02 21:03:08 +0000544
545 return 0;
546
malcb8e59f12008-07-02 21:03:08 +0000547 fail1:
548 return -1;
549}
550
Kővágó, Zoltán5706db12015-06-03 23:03:47 +0200551static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
malcb8e59f12008-07-02 21:03:08 +0000552{
553 int error;
Kővágó, Zoltán9a644c42015-06-03 23:03:49 +0200554 pa_sample_spec ss;
Martin Schrodtade10302019-03-15 09:46:53 +0100555 pa_buffer_attr ba;
malc1ea879e2008-12-03 22:48:44 +0000556 struct audsettings obt_as = *as;
malcb8e59f12008-07-02 21:03:08 +0000557 PAVoiceIn *pa = (PAVoiceIn *) hw;
Kővágó, Zoltán9a644c42015-06-03 23:03:49 +0200558 paaudio *g = pa->g = drv_opaque;
Kővágó, Zoltán2c324b22019-03-08 23:34:21 +0100559 AudiodevPaOptions *popts = &g->dev->u.pa;
560 AudiodevPaPerDirectionOptions *ppdo = popts->in;
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200561 PAConnection *c = g->conn;
malcb8e59f12008-07-02 21:03:08 +0000562
563 ss.format = audfmt_to_pa (as->fmt, as->endianness);
564 ss.channels = as->nchannels;
565 ss.rate = as->freq;
566
Volker Rümelind9a8b272021-01-10 11:02:35 +0100567 ba.fragsize = pa_usec_to_bytes((g->dev->timer_period >> 1) * 3, &ss);
568 ba.maxlength = pa_usec_to_bytes(
569 MAX(ppdo->latency, g->dev->timer_period * 3), &ss);
Martin Schrodtade10302019-03-15 09:46:53 +0100570 ba.minreq = -1;
571 ba.prebuf = -1;
572
malcb8e59f12008-07-02 21:03:08 +0000573 obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
574
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200575 pa->stream = qpa_simple_new (
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200576 c,
Kővágó, Zoltánf47dffe82019-09-11 01:26:20 +0200577 ppdo->has_stream_name ? ppdo->stream_name : g->dev->id,
malcb8e59f12008-07-02 21:03:08 +0000578 PA_STREAM_RECORD,
Kővágó, Zoltán2c324b22019-03-08 23:34:21 +0100579 ppdo->has_name ? ppdo->name : NULL,
malcb8e59f12008-07-02 21:03:08 +0000580 &ss,
Martin Schrodtade10302019-03-15 09:46:53 +0100581 &ba, /* buffering attributes */
malcb8e59f12008-07-02 21:03:08 +0000582 &error
583 );
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200584 if (!pa->stream) {
malcb8e59f12008-07-02 21:03:08 +0000585 qpa_logerr (error, "pa_simple_new for capture failed\n");
586 goto fail1;
587 }
588
589 audio_pcm_init_info (&hw->info, &obt_as);
Volker Rümelincffd2fd2021-01-10 11:02:33 +0100590 /*
591 * This is wrong. hw->samples counts in frames. hw->samples will be
592 * number of channels times larger than expected.
593 */
Volker Rümelina76e6b82020-01-23 08:49:37 +0100594 hw->samples = audio_buffer_samples(
Volker Rümelin521ce712021-01-10 11:02:32 +0100595 qapi_AudiodevPaPerDirectionOptions_base(ppdo), &obt_as, 46440);
malcb8e59f12008-07-02 21:03:08 +0000596
597 return 0;
598
malcb8e59f12008-07-02 21:03:08 +0000599 fail1:
600 return -1;
601}
602
Kővágó, Zoltán8692bf72019-08-19 01:06:53 +0200603static void qpa_simple_disconnect(PAConnection *c, pa_stream *stream)
604{
605 int err;
606
Kővágó, Zoltán8692bf72019-08-19 01:06:53 +0200607 /*
608 * wait until actually connects. workaround pa bug #247
609 * https://gitlab.freedesktop.org/pulseaudio/pulseaudio/issues/247
610 */
611 while (pa_stream_get_state(stream) == PA_STREAM_CREATING) {
612 pa_threaded_mainloop_wait(c->mainloop);
613 }
614
615 err = pa_stream_disconnect(stream);
616 if (err != 0) {
617 dolog("Failed to disconnect! err=%d\n", err);
618 }
619 pa_stream_unref(stream);
Kővágó, Zoltán8692bf72019-08-19 01:06:53 +0200620}
621
malcb8e59f12008-07-02 21:03:08 +0000622static void qpa_fini_out (HWVoiceOut *hw)
623{
malcb8e59f12008-07-02 21:03:08 +0000624 PAVoiceOut *pa = (PAVoiceOut *) hw;
625
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200626 if (pa->stream) {
Volker Rümelin4db3e632020-01-04 10:11:20 +0100627 PAConnection *c = pa->g->conn;
628
629 pa_threaded_mainloop_lock(c->mainloop);
630 qpa_simple_disconnect(c, pa->stream);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200631 pa->stream = NULL;
Volker Rümelin4db3e632020-01-04 10:11:20 +0100632 pa_threaded_mainloop_unlock(c->mainloop);
malcb8e59f12008-07-02 21:03:08 +0000633 }
malcb8e59f12008-07-02 21:03:08 +0000634}
635
636static void qpa_fini_in (HWVoiceIn *hw)
637{
malcb8e59f12008-07-02 21:03:08 +0000638 PAVoiceIn *pa = (PAVoiceIn *) hw;
639
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200640 if (pa->stream) {
Volker Rümelin4db3e632020-01-04 10:11:20 +0100641 PAConnection *c = pa->g->conn;
642
643 pa_threaded_mainloop_lock(c->mainloop);
644 if (pa->read_length) {
645 int r = pa_stream_drop(pa->stream);
646 if (r) {
647 qpa_logerr(pa_context_errno(c->context),
648 "pa_stream_drop failed\n");
649 }
650 pa->read_length = 0;
651 }
652 qpa_simple_disconnect(c, pa->stream);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200653 pa->stream = NULL;
Volker Rümelin4db3e632020-01-04 10:11:20 +0100654 pa_threaded_mainloop_unlock(c->mainloop);
malcb8e59f12008-07-02 21:03:08 +0000655 }
malcb8e59f12008-07-02 21:03:08 +0000656}
657
Kővágó, Zoltáncecc1e72019-10-13 21:58:01 +0200658static void qpa_volume_out(HWVoiceOut *hw, Volume *vol)
malcb8e59f12008-07-02 21:03:08 +0000659{
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200660 PAVoiceOut *pa = (PAVoiceOut *) hw;
661 pa_operation *op;
662 pa_cvolume v;
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200663 PAConnection *c = pa->g->conn;
Kővágó, Zoltáncecc1e72019-10-13 21:58:01 +0200664 int i;
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200665
Gerd Hoffmann8f473dd2012-05-03 22:41:28 +0200666#ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */
667 pa_cvolume_init (&v); /* function is present in 0.9.13+ */
668#endif
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200669
Kővágó, Zoltáncecc1e72019-10-13 21:58:01 +0200670 v.channels = vol->channels;
671 for (i = 0; i < vol->channels; ++i) {
672 v.values[i] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->vol[i]) / 255;
673 }
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200674
Kővágó, Zoltán571a8c52019-09-19 23:24:22 +0200675 pa_threaded_mainloop_lock(c->mainloop);
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200676
Kővágó, Zoltán571a8c52019-09-19 23:24:22 +0200677 op = pa_context_set_sink_input_volume(c->context,
678 pa_stream_get_index(pa->stream),
679 &v, NULL, NULL);
680 if (!op) {
681 qpa_logerr(pa_context_errno(c->context),
682 "set_sink_input_volume() failed\n");
683 } else {
684 pa_operation_unref(op);
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200685 }
Kővágó, Zoltán571a8c52019-09-19 23:24:22 +0200686
687 op = pa_context_set_sink_input_mute(c->context,
688 pa_stream_get_index(pa->stream),
689 vol->mute, NULL, NULL);
690 if (!op) {
691 qpa_logerr(pa_context_errno(c->context),
692 "set_sink_input_mute() failed\n");
693 } else {
694 pa_operation_unref(op);
695 }
696
697 pa_threaded_mainloop_unlock(c->mainloop);
malcb8e59f12008-07-02 21:03:08 +0000698}
699
Kővágó, Zoltáncecc1e72019-10-13 21:58:01 +0200700static void qpa_volume_in(HWVoiceIn *hw, Volume *vol)
malcb8e59f12008-07-02 21:03:08 +0000701{
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200702 PAVoiceIn *pa = (PAVoiceIn *) hw;
703 pa_operation *op;
704 pa_cvolume v;
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200705 PAConnection *c = pa->g->conn;
Kővágó, Zoltáncecc1e72019-10-13 21:58:01 +0200706 int i;
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200707
Gerd Hoffmann8f473dd2012-05-03 22:41:28 +0200708#ifdef PA_CHECK_VERSION
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200709 pa_cvolume_init (&v);
Gerd Hoffmann8f473dd2012-05-03 22:41:28 +0200710#endif
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200711
Kővágó, Zoltáncecc1e72019-10-13 21:58:01 +0200712 v.channels = vol->channels;
713 for (i = 0; i < vol->channels; ++i) {
714 v.values[i] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * vol->vol[i]) / 255;
715 }
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200716
Kővágó, Zoltán571a8c52019-09-19 23:24:22 +0200717 pa_threaded_mainloop_lock(c->mainloop);
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200718
Kővágó, Zoltán571a8c52019-09-19 23:24:22 +0200719 op = pa_context_set_source_output_volume(c->context,
720 pa_stream_get_index(pa->stream),
721 &v, NULL, NULL);
722 if (!op) {
723 qpa_logerr(pa_context_errno(c->context),
724 "set_source_output_volume() failed\n");
725 } else {
726 pa_operation_unref(op);
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200727 }
Kővágó, Zoltán571a8c52019-09-19 23:24:22 +0200728
729 op = pa_context_set_source_output_mute(c->context,
730 pa_stream_get_index(pa->stream),
731 vol->mute, NULL, NULL);
732 if (!op) {
733 qpa_logerr(pa_context_errno(c->context),
734 "set_source_output_mute() failed\n");
735 } else {
736 pa_operation_unref(op);
737 }
738
739 pa_threaded_mainloop_unlock(c->mainloop);
malcb8e59f12008-07-02 21:03:08 +0000740}
741
Martin Schrodtbaea0322019-03-15 09:46:51 +0100742static int qpa_validate_per_direction_opts(Audiodev *dev,
743 AudiodevPaPerDirectionOptions *pdo)
744{
Martin Schrodtf6142772019-03-15 09:46:52 +0100745 if (!pdo->has_latency) {
746 pdo->has_latency = true;
747 pdo->latency = 15000;
748 }
Martin Schrodtbaea0322019-03-15 09:46:51 +0100749 return 1;
750}
751
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200752/* common */
753static void *qpa_conn_init(const char *server)
754{
755 PAConnection *c = g_malloc0(sizeof(PAConnection));
756 QTAILQ_INSERT_TAIL(&pa_conns, c, list);
757
758 c->mainloop = pa_threaded_mainloop_new();
759 if (!c->mainloop) {
760 goto fail;
761 }
762
763 c->context = pa_context_new(pa_threaded_mainloop_get_api(c->mainloop),
Volker Rümelin37a54d02021-05-17 21:46:03 +0200764 audio_application_name());
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200765 if (!c->context) {
766 goto fail;
767 }
768
769 pa_context_set_state_callback(c->context, context_state_cb, c);
770
771 if (pa_context_connect(c->context, server, 0, NULL) < 0) {
772 qpa_logerr(pa_context_errno(c->context),
773 "pa_context_connect() failed\n");
774 goto fail;
775 }
776
777 pa_threaded_mainloop_lock(c->mainloop);
778
779 if (pa_threaded_mainloop_start(c->mainloop) < 0) {
780 goto unlock_and_fail;
781 }
782
783 for (;;) {
784 pa_context_state_t state;
785
786 state = pa_context_get_state(c->context);
787
788 if (state == PA_CONTEXT_READY) {
789 break;
790 }
791
792 if (!PA_CONTEXT_IS_GOOD(state)) {
793 qpa_logerr(pa_context_errno(c->context),
794 "Wrong context state\n");
795 goto unlock_and_fail;
796 }
797
798 /* Wait until the context is ready */
799 pa_threaded_mainloop_wait(c->mainloop);
800 }
801
802 pa_threaded_mainloop_unlock(c->mainloop);
803 return c;
804
805unlock_and_fail:
806 pa_threaded_mainloop_unlock(c->mainloop);
807fail:
808 AUD_log (AUDIO_CAP, "Failed to initialize PA context");
809 qpa_conn_fini(c);
810 return NULL;
811}
812
Kővágó, Zoltán71830222019-03-08 23:34:15 +0100813static void *qpa_audio_init(Audiodev *dev)
malcb8e59f12008-07-02 21:03:08 +0000814{
Kővágó, Zoltán2c324b22019-03-08 23:34:21 +0100815 paaudio *g;
816 AudiodevPaOptions *popts = &dev->u.pa;
817 const char *server;
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200818 PAConnection *c;
819
820 assert(dev->driver == AUDIODEV_DRIVER_PA);
Kővágó, Zoltán2c324b22019-03-08 23:34:21 +0100821
822 if (!popts->has_server) {
Gerd Hoffmannd1755052019-01-24 12:20:53 +0100823 char pidfile[64];
824 char *runtime;
825 struct stat st;
826
827 runtime = getenv("XDG_RUNTIME_DIR");
828 if (!runtime) {
829 return NULL;
830 }
831 snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime);
832 if (stat(pidfile, &st) != 0) {
833 return NULL;
834 }
835 }
836
Martin Schrodtbaea0322019-03-15 09:46:51 +0100837 if (!qpa_validate_per_direction_opts(dev, popts->in)) {
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200838 return NULL;
Martin Schrodtbaea0322019-03-15 09:46:51 +0100839 }
840 if (!qpa_validate_per_direction_opts(dev, popts->out)) {
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200841 return NULL;
Martin Schrodtbaea0322019-03-15 09:46:51 +0100842 }
843
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200844 g = g_malloc0(sizeof(paaudio));
845 server = popts->has_server ? popts->server : NULL;
846
Kővágó, Zoltán2c324b22019-03-08 23:34:21 +0100847 g->dev = dev;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200848
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200849 QTAILQ_FOREACH(c, &pa_conns, list) {
850 if (server == NULL || c->server == NULL ?
851 server == c->server :
852 strcmp(server, c->server) == 0) {
853 g->conn = c;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200854 break;
855 }
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200856 }
857 if (!g->conn) {
858 g->conn = qpa_conn_init(server);
859 }
860 if (!g->conn) {
861 g_free(g);
862 return NULL;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200863 }
864
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200865 ++g->conn->refcount;
Kővágó, Zoltán9a644c42015-06-03 23:03:49 +0200866 return g;
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200867}
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200868
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200869static void qpa_conn_fini(PAConnection *c)
870{
871 if (c->mainloop) {
872 pa_threaded_mainloop_stop(c->mainloop);
873 }
874
875 if (c->context) {
876 pa_context_disconnect(c->context);
877 pa_context_unref(c->context);
878 }
879
880 if (c->mainloop) {
881 pa_threaded_mainloop_free(c->mainloop);
882 }
883
884 QTAILQ_REMOVE(&pa_conns, c, list);
885 g_free(c);
malcb8e59f12008-07-02 21:03:08 +0000886}
887
888static void qpa_audio_fini (void *opaque)
889{
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200890 paaudio *g = opaque;
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200891 PAConnection *c = g->conn;
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200892
Kővágó, Zoltán9d34e6d2019-08-19 01:06:50 +0200893 if (--c->refcount == 0) {
894 qpa_conn_fini(c);
Marc-André Lureauea9ebc22012-04-17 14:32:41 +0200895 }
896
Kővágó, Zoltán9a644c42015-06-03 23:03:49 +0200897 g_free(g);
malcb8e59f12008-07-02 21:03:08 +0000898}
899
blueswir135f4b582008-10-06 18:08:30 +0000900static struct audio_pcm_ops qpa_pcm_ops = {
Juan Quintela1dd3e4d2009-08-11 02:31:15 +0200901 .init_out = qpa_init_out,
902 .fini_out = qpa_fini_out,
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200903 .write = qpa_write,
Kővágó, Zoltán337e8de2019-10-13 21:58:00 +0200904 .get_buffer_out = qpa_get_buffer_out,
Volker Rümelinbea29e92021-01-10 11:02:29 +0100905 .put_buffer_out = qpa_put_buffer_out,
Kővágó, Zoltán571a8c52019-09-19 23:24:22 +0200906 .volume_out = qpa_volume_out,
Juan Quintela1dd3e4d2009-08-11 02:31:15 +0200907
908 .init_in = qpa_init_in,
909 .fini_in = qpa_fini_in,
Kővágó, Zoltán49ddd7e2019-09-19 23:24:15 +0200910 .read = qpa_read,
Kővágó, Zoltán337e8de2019-10-13 21:58:00 +0200911 .get_buffer_in = qpa_get_buffer_in,
912 .put_buffer_in = qpa_put_buffer_in,
Kővágó, Zoltán571a8c52019-09-19 23:24:22 +0200913 .volume_in = qpa_volume_in
malcb8e59f12008-07-02 21:03:08 +0000914};
915
Gerd Hoffmannd3893a32018-03-06 08:40:47 +0100916static struct audio_driver pa_audio_driver = {
Juan Quintelabee37f32009-08-11 02:31:14 +0200917 .name = "pa",
918 .descr = "http://www.pulseaudio.org/",
Juan Quintelabee37f32009-08-11 02:31:14 +0200919 .init = qpa_audio_init,
920 .fini = qpa_audio_fini,
921 .pcm_ops = &qpa_pcm_ops,
Michael S. Tsirkin1a4ea1e2009-10-13 16:08:55 +0200922 .can_be_default = 1,
Juan Quintelabee37f32009-08-11 02:31:14 +0200923 .max_voices_out = INT_MAX,
924 .max_voices_in = INT_MAX,
925 .voice_size_out = sizeof (PAVoiceOut),
Marc-André Lureau6e7a7f32012-04-17 14:32:43 +0200926 .voice_size_in = sizeof (PAVoiceIn),
malcb8e59f12008-07-02 21:03:08 +0000927};
Gerd Hoffmannd3893a32018-03-06 08:40:47 +0100928
929static void register_audio_pa(void)
930{
931 audio_driver_register(&pa_audio_driver);
932}
933type_init(register_audio_pa);