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 | }; |