root / audio / paaudio.c @ ed23fbd9
History | View | Annotate | Download (11.2 kB)
1 | b8e59f18 | malc | /* public domain */
|
---|---|---|---|
2 | b8e59f18 | malc | #include "qemu-common.h" |
3 | b8e59f18 | malc | #include "audio.h" |
4 | b8e59f18 | malc | |
5 | b8e59f18 | malc | #include <pulse/simple.h> |
6 | b8e59f18 | malc | #include <pulse/error.h> |
7 | b8e59f18 | malc | |
8 | b8e59f18 | malc | #define AUDIO_CAP "pulseaudio" |
9 | b8e59f18 | malc | #include "audio_int.h" |
10 | b8e59f18 | malc | #include "audio_pt_int.h" |
11 | b8e59f18 | malc | |
12 | b8e59f18 | malc | typedef struct { |
13 | b8e59f18 | malc | HWVoiceOut hw; |
14 | b8e59f18 | malc | int done;
|
15 | b8e59f18 | malc | int live;
|
16 | b8e59f18 | malc | int decr;
|
17 | b8e59f18 | malc | int rpos;
|
18 | b8e59f18 | malc | pa_simple *s; |
19 | b8e59f18 | malc | void *pcm_buf;
|
20 | b8e59f18 | malc | struct audio_pt pt;
|
21 | b8e59f18 | malc | } PAVoiceOut; |
22 | b8e59f18 | malc | |
23 | b8e59f18 | malc | typedef struct { |
24 | b8e59f18 | malc | HWVoiceIn hw; |
25 | b8e59f18 | malc | int done;
|
26 | b8e59f18 | malc | int dead;
|
27 | b8e59f18 | malc | int incr;
|
28 | b8e59f18 | malc | int wpos;
|
29 | b8e59f18 | malc | pa_simple *s; |
30 | b8e59f18 | malc | void *pcm_buf;
|
31 | b8e59f18 | malc | struct audio_pt pt;
|
32 | b8e59f18 | malc | } PAVoiceIn; |
33 | b8e59f18 | malc | |
34 | b8e59f18 | malc | static struct { |
35 | b8e59f18 | malc | int samples;
|
36 | b8e59f18 | malc | int divisor;
|
37 | b8e59f18 | malc | char *server;
|
38 | b8e59f18 | malc | char *sink;
|
39 | b8e59f18 | malc | char *source;
|
40 | b8e59f18 | malc | } conf = { |
41 | b8e59f18 | malc | 1024,
|
42 | b8e59f18 | malc | 2,
|
43 | b8e59f18 | malc | NULL,
|
44 | b8e59f18 | malc | NULL,
|
45 | b8e59f18 | malc | NULL
|
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 | b8e59f18 | malc | static void *qpa_thread_out (void *arg) |
60 | b8e59f18 | malc | { |
61 | b8e59f18 | malc | PAVoiceOut *pa = arg; |
62 | b8e59f18 | malc | HWVoiceOut *hw = &pa->hw; |
63 | b8e59f18 | malc | int threshold;
|
64 | b8e59f18 | malc | |
65 | b8e59f18 | malc | threshold = conf.divisor ? hw->samples / conf.divisor : 0;
|
66 | b8e59f18 | malc | |
67 | b8e59f18 | malc | if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
|
68 | b8e59f18 | malc | return NULL; |
69 | b8e59f18 | malc | } |
70 | b8e59f18 | malc | |
71 | b8e59f18 | malc | for (;;) {
|
72 | b8e59f18 | malc | int decr, to_mix, rpos;
|
73 | b8e59f18 | malc | |
74 | b8e59f18 | malc | for (;;) {
|
75 | b8e59f18 | malc | if (pa->done) {
|
76 | b8e59f18 | malc | goto exit;
|
77 | b8e59f18 | malc | } |
78 | b8e59f18 | malc | |
79 | b8e59f18 | malc | if (pa->live > threshold) {
|
80 | b8e59f18 | malc | break;
|
81 | b8e59f18 | malc | } |
82 | b8e59f18 | malc | |
83 | b8e59f18 | malc | if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
|
84 | b8e59f18 | malc | goto exit;
|
85 | b8e59f18 | malc | } |
86 | b8e59f18 | malc | } |
87 | b8e59f18 | malc | |
88 | b8e59f18 | malc | decr = to_mix = pa->live; |
89 | b8e59f18 | malc | rpos = hw->rpos; |
90 | b8e59f18 | malc | |
91 | b8e59f18 | malc | if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
|
92 | b8e59f18 | malc | return NULL; |
93 | b8e59f18 | malc | } |
94 | b8e59f18 | malc | |
95 | b8e59f18 | malc | while (to_mix) {
|
96 | b8e59f18 | malc | int error;
|
97 | b8e59f18 | malc | int chunk = audio_MIN (to_mix, hw->samples - rpos);
|
98 | b8e59f18 | malc | st_sample_t *src = hw->mix_buf + rpos; |
99 | b8e59f18 | malc | |
100 | b8e59f18 | malc | hw->clip (pa->pcm_buf, src, chunk); |
101 | b8e59f18 | malc | |
102 | b8e59f18 | malc | if (pa_simple_write (pa->s, pa->pcm_buf,
|
103 | b8e59f18 | malc | chunk << hw->info.shift, &error) < 0) {
|
104 | b8e59f18 | malc | qpa_logerr (error, "pa_simple_write failed\n");
|
105 | b8e59f18 | malc | return NULL; |
106 | b8e59f18 | malc | } |
107 | b8e59f18 | malc | |
108 | b8e59f18 | malc | rpos = (rpos + chunk) % hw->samples; |
109 | b8e59f18 | malc | to_mix -= chunk; |
110 | b8e59f18 | malc | } |
111 | b8e59f18 | malc | |
112 | b8e59f18 | malc | if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
|
113 | b8e59f18 | malc | return NULL; |
114 | b8e59f18 | malc | } |
115 | b8e59f18 | malc | |
116 | b8e59f18 | malc | pa->rpos = rpos; |
117 | b8e59f18 | malc | pa->live -= decr; |
118 | b8e59f18 | malc | pa->decr += decr; |
119 | b8e59f18 | malc | } |
120 | b8e59f18 | malc | |
121 | b8e59f18 | malc | exit:
|
122 | b8e59f18 | malc | audio_pt_unlock (&pa->pt, AUDIO_FUNC); |
123 | b8e59f18 | malc | return NULL; |
124 | b8e59f18 | malc | } |
125 | b8e59f18 | malc | |
126 | b8e59f18 | malc | static int qpa_run_out (HWVoiceOut *hw) |
127 | b8e59f18 | malc | { |
128 | b8e59f18 | malc | int live, decr;
|
129 | b8e59f18 | malc | PAVoiceOut *pa = (PAVoiceOut *) hw; |
130 | b8e59f18 | malc | |
131 | b8e59f18 | malc | if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
|
132 | b8e59f18 | malc | return 0; |
133 | b8e59f18 | malc | } |
134 | b8e59f18 | malc | |
135 | b8e59f18 | malc | live = audio_pcm_hw_get_live_out (hw); |
136 | b8e59f18 | malc | decr = audio_MIN (live, pa->decr); |
137 | b8e59f18 | malc | pa->decr -= decr; |
138 | b8e59f18 | malc | pa->live = live - decr; |
139 | b8e59f18 | malc | hw->rpos = pa->rpos; |
140 | b8e59f18 | malc | if (pa->live > 0) { |
141 | b8e59f18 | malc | audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); |
142 | b8e59f18 | malc | } |
143 | b8e59f18 | malc | else {
|
144 | b8e59f18 | malc | audio_pt_unlock (&pa->pt, AUDIO_FUNC); |
145 | b8e59f18 | malc | } |
146 | b8e59f18 | malc | return decr;
|
147 | b8e59f18 | malc | } |
148 | b8e59f18 | malc | |
149 | b8e59f18 | malc | static int qpa_write (SWVoiceOut *sw, void *buf, int len) |
150 | b8e59f18 | malc | { |
151 | b8e59f18 | malc | return audio_pcm_sw_write (sw, buf, len);
|
152 | b8e59f18 | malc | } |
153 | b8e59f18 | malc | |
154 | b8e59f18 | malc | /* capture */
|
155 | b8e59f18 | malc | static void *qpa_thread_in (void *arg) |
156 | b8e59f18 | malc | { |
157 | b8e59f18 | malc | PAVoiceIn *pa = arg; |
158 | b8e59f18 | malc | HWVoiceIn *hw = &pa->hw; |
159 | b8e59f18 | malc | int threshold;
|
160 | b8e59f18 | malc | |
161 | b8e59f18 | malc | threshold = conf.divisor ? hw->samples / conf.divisor : 0;
|
162 | b8e59f18 | malc | |
163 | b8e59f18 | malc | if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
|
164 | b8e59f18 | malc | return NULL; |
165 | b8e59f18 | malc | } |
166 | b8e59f18 | malc | |
167 | b8e59f18 | malc | for (;;) {
|
168 | b8e59f18 | malc | int incr, to_grab, wpos;
|
169 | b8e59f18 | malc | |
170 | b8e59f18 | malc | for (;;) {
|
171 | b8e59f18 | malc | if (pa->done) {
|
172 | b8e59f18 | malc | goto exit;
|
173 | b8e59f18 | malc | } |
174 | b8e59f18 | malc | |
175 | b8e59f18 | malc | if (pa->dead > threshold) {
|
176 | b8e59f18 | malc | break;
|
177 | b8e59f18 | malc | } |
178 | b8e59f18 | malc | |
179 | b8e59f18 | malc | if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
|
180 | b8e59f18 | malc | goto exit;
|
181 | b8e59f18 | malc | } |
182 | b8e59f18 | malc | } |
183 | b8e59f18 | malc | |
184 | b8e59f18 | malc | incr = to_grab = pa->dead; |
185 | b8e59f18 | malc | wpos = hw->wpos; |
186 | b8e59f18 | malc | |
187 | b8e59f18 | malc | if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
|
188 | b8e59f18 | malc | return NULL; |
189 | b8e59f18 | malc | } |
190 | b8e59f18 | malc | |
191 | b8e59f18 | malc | while (to_grab) {
|
192 | b8e59f18 | malc | int error;
|
193 | b8e59f18 | malc | int chunk = audio_MIN (to_grab, hw->samples - wpos);
|
194 | b8e59f18 | malc | void *buf = advance (pa->pcm_buf, wpos);
|
195 | b8e59f18 | malc | |
196 | b8e59f18 | malc | if (pa_simple_read (pa->s, buf,
|
197 | b8e59f18 | malc | chunk << hw->info.shift, &error) < 0) {
|
198 | b8e59f18 | malc | qpa_logerr (error, "pa_simple_read failed\n");
|
199 | b8e59f18 | malc | return NULL; |
200 | b8e59f18 | malc | } |
201 | b8e59f18 | malc | |
202 | b8e59f18 | malc | hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume); |
203 | b8e59f18 | malc | wpos = (wpos + chunk) % hw->samples; |
204 | b8e59f18 | malc | to_grab -= chunk; |
205 | b8e59f18 | malc | } |
206 | b8e59f18 | malc | |
207 | b8e59f18 | malc | if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
|
208 | b8e59f18 | malc | return NULL; |
209 | b8e59f18 | malc | } |
210 | b8e59f18 | malc | |
211 | b8e59f18 | malc | pa->wpos = wpos; |
212 | b8e59f18 | malc | pa->dead -= incr; |
213 | b8e59f18 | malc | pa->incr += incr; |
214 | b8e59f18 | malc | } |
215 | b8e59f18 | malc | |
216 | b8e59f18 | malc | exit:
|
217 | b8e59f18 | malc | audio_pt_unlock (&pa->pt, AUDIO_FUNC); |
218 | b8e59f18 | malc | return NULL; |
219 | b8e59f18 | malc | } |
220 | b8e59f18 | malc | |
221 | b8e59f18 | malc | static int qpa_run_in (HWVoiceIn *hw) |
222 | b8e59f18 | malc | { |
223 | b8e59f18 | malc | int live, incr, dead;
|
224 | b8e59f18 | malc | PAVoiceIn *pa = (PAVoiceIn *) hw; |
225 | b8e59f18 | malc | |
226 | b8e59f18 | malc | if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
|
227 | b8e59f18 | malc | return 0; |
228 | b8e59f18 | malc | } |
229 | b8e59f18 | malc | |
230 | b8e59f18 | malc | live = audio_pcm_hw_get_live_in (hw); |
231 | b8e59f18 | malc | dead = hw->samples - live; |
232 | b8e59f18 | malc | incr = audio_MIN (dead, pa->incr); |
233 | b8e59f18 | malc | pa->incr -= incr; |
234 | b8e59f18 | malc | pa->dead = dead - incr; |
235 | b8e59f18 | malc | hw->wpos = pa->wpos; |
236 | b8e59f18 | malc | if (pa->dead > 0) { |
237 | b8e59f18 | malc | audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); |
238 | b8e59f18 | malc | } |
239 | b8e59f18 | malc | else {
|
240 | b8e59f18 | malc | audio_pt_unlock (&pa->pt, AUDIO_FUNC); |
241 | b8e59f18 | malc | } |
242 | b8e59f18 | malc | return incr;
|
243 | b8e59f18 | malc | } |
244 | b8e59f18 | malc | |
245 | b8e59f18 | malc | static int qpa_read (SWVoiceIn *sw, void *buf, int len) |
246 | b8e59f18 | malc | { |
247 | b8e59f18 | malc | return audio_pcm_sw_read (sw, buf, len);
|
248 | b8e59f18 | malc | } |
249 | b8e59f18 | malc | |
250 | b8e59f18 | malc | static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) |
251 | b8e59f18 | malc | { |
252 | b8e59f18 | malc | int format;
|
253 | b8e59f18 | malc | |
254 | b8e59f18 | malc | switch (afmt) {
|
255 | b8e59f18 | malc | case AUD_FMT_S8:
|
256 | b8e59f18 | malc | case AUD_FMT_U8:
|
257 | b8e59f18 | malc | format = PA_SAMPLE_U8; |
258 | b8e59f18 | malc | break;
|
259 | b8e59f18 | malc | case AUD_FMT_S16:
|
260 | b8e59f18 | malc | case AUD_FMT_U16:
|
261 | b8e59f18 | malc | format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; |
262 | b8e59f18 | malc | break;
|
263 | b8e59f18 | malc | case AUD_FMT_S32:
|
264 | b8e59f18 | malc | case AUD_FMT_U32:
|
265 | b8e59f18 | malc | format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; |
266 | b8e59f18 | malc | break;
|
267 | b8e59f18 | malc | default:
|
268 | b8e59f18 | malc | dolog ("Internal logic error: Bad audio format %d\n", afmt);
|
269 | b8e59f18 | malc | format = PA_SAMPLE_U8; |
270 | b8e59f18 | malc | break;
|
271 | b8e59f18 | malc | } |
272 | b8e59f18 | malc | return format;
|
273 | b8e59f18 | malc | } |
274 | b8e59f18 | malc | |
275 | b8e59f18 | malc | static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) |
276 | b8e59f18 | malc | { |
277 | b8e59f18 | malc | switch (fmt) {
|
278 | b8e59f18 | malc | case PA_SAMPLE_U8:
|
279 | b8e59f18 | malc | return AUD_FMT_U8;
|
280 | b8e59f18 | malc | case PA_SAMPLE_S16BE:
|
281 | b8e59f18 | malc | *endianness = 1;
|
282 | b8e59f18 | malc | return AUD_FMT_S16;
|
283 | b8e59f18 | malc | case PA_SAMPLE_S16LE:
|
284 | b8e59f18 | malc | *endianness = 0;
|
285 | b8e59f18 | malc | return AUD_FMT_S16;
|
286 | b8e59f18 | malc | case PA_SAMPLE_S32BE:
|
287 | b8e59f18 | malc | *endianness = 1;
|
288 | b8e59f18 | malc | return AUD_FMT_S32;
|
289 | b8e59f18 | malc | case PA_SAMPLE_S32LE:
|
290 | b8e59f18 | malc | *endianness = 0;
|
291 | b8e59f18 | malc | return AUD_FMT_S32;
|
292 | b8e59f18 | malc | default:
|
293 | b8e59f18 | malc | dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt);
|
294 | b8e59f18 | malc | return AUD_FMT_U8;
|
295 | b8e59f18 | malc | } |
296 | b8e59f18 | malc | } |
297 | b8e59f18 | malc | |
298 | b8e59f18 | malc | static int qpa_init_out (HWVoiceOut *hw, audsettings_t *as) |
299 | b8e59f18 | malc | { |
300 | b8e59f18 | malc | int error;
|
301 | b8e59f18 | malc | static pa_sample_spec ss;
|
302 | b8e59f18 | malc | audsettings_t obt_as = *as; |
303 | b8e59f18 | malc | PAVoiceOut *pa = (PAVoiceOut *) hw; |
304 | b8e59f18 | malc | |
305 | b8e59f18 | malc | ss.format = audfmt_to_pa (as->fmt, as->endianness); |
306 | b8e59f18 | malc | ss.channels = as->nchannels; |
307 | b8e59f18 | malc | ss.rate = as->freq; |
308 | b8e59f18 | malc | |
309 | b8e59f18 | malc | obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); |
310 | b8e59f18 | malc | |
311 | b8e59f18 | malc | pa->s = pa_simple_new ( |
312 | b8e59f18 | malc | conf.server, |
313 | b8e59f18 | malc | "qemu",
|
314 | b8e59f18 | malc | PA_STREAM_PLAYBACK, |
315 | b8e59f18 | malc | conf.sink, |
316 | b8e59f18 | malc | "pcm.playback",
|
317 | b8e59f18 | malc | &ss, |
318 | b8e59f18 | malc | NULL, /* channel map */ |
319 | b8e59f18 | malc | NULL, /* buffering attributes */ |
320 | b8e59f18 | malc | &error |
321 | b8e59f18 | malc | ); |
322 | b8e59f18 | malc | if (!pa->s) {
|
323 | b8e59f18 | malc | qpa_logerr (error, "pa_simple_new for playback failed\n");
|
324 | b8e59f18 | malc | goto fail1;
|
325 | b8e59f18 | malc | } |
326 | b8e59f18 | malc | |
327 | b8e59f18 | malc | audio_pcm_init_info (&hw->info, &obt_as); |
328 | b8e59f18 | malc | hw->samples = conf.samples; |
329 | b8e59f18 | malc | pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
330 | b8e59f18 | malc | if (!pa->pcm_buf) {
|
331 | b8e59f18 | malc | dolog ("Could not allocate buffer (%d bytes)\n",
|
332 | b8e59f18 | malc | hw->samples << hw->info.shift); |
333 | b8e59f18 | malc | goto fail2;
|
334 | b8e59f18 | malc | } |
335 | b8e59f18 | malc | |
336 | b8e59f18 | malc | if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
|
337 | b8e59f18 | malc | goto fail3;
|
338 | b8e59f18 | malc | } |
339 | b8e59f18 | malc | |
340 | b8e59f18 | malc | return 0; |
341 | b8e59f18 | malc | |
342 | b8e59f18 | malc | fail3:
|
343 | b8e59f18 | malc | free (pa->pcm_buf); |
344 | b8e59f18 | malc | pa->pcm_buf = NULL;
|
345 | b8e59f18 | malc | fail2:
|
346 | b8e59f18 | malc | pa_simple_free (pa->s); |
347 | b8e59f18 | malc | pa->s = NULL;
|
348 | b8e59f18 | malc | fail1:
|
349 | b8e59f18 | malc | return -1; |
350 | b8e59f18 | malc | } |
351 | b8e59f18 | malc | |
352 | b8e59f18 | malc | static int qpa_init_in (HWVoiceIn *hw, audsettings_t *as) |
353 | b8e59f18 | malc | { |
354 | b8e59f18 | malc | int error;
|
355 | b8e59f18 | malc | static pa_sample_spec ss;
|
356 | b8e59f18 | malc | audsettings_t obt_as = *as; |
357 | b8e59f18 | malc | PAVoiceIn *pa = (PAVoiceIn *) hw; |
358 | b8e59f18 | malc | |
359 | b8e59f18 | malc | ss.format = audfmt_to_pa (as->fmt, as->endianness); |
360 | b8e59f18 | malc | ss.channels = as->nchannels; |
361 | b8e59f18 | malc | ss.rate = as->freq; |
362 | b8e59f18 | malc | |
363 | b8e59f18 | malc | obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); |
364 | b8e59f18 | malc | |
365 | b8e59f18 | malc | pa->s = pa_simple_new ( |
366 | b8e59f18 | malc | conf.server, |
367 | b8e59f18 | malc | "qemu",
|
368 | b8e59f18 | malc | PA_STREAM_RECORD, |
369 | b8e59f18 | malc | conf.source, |
370 | b8e59f18 | malc | "pcm.capture",
|
371 | b8e59f18 | malc | &ss, |
372 | b8e59f18 | malc | NULL, /* channel map */ |
373 | b8e59f18 | malc | NULL, /* buffering attributes */ |
374 | b8e59f18 | malc | &error |
375 | b8e59f18 | malc | ); |
376 | b8e59f18 | malc | if (!pa->s) {
|
377 | b8e59f18 | malc | qpa_logerr (error, "pa_simple_new for capture failed\n");
|
378 | b8e59f18 | malc | goto fail1;
|
379 | b8e59f18 | malc | } |
380 | b8e59f18 | malc | |
381 | b8e59f18 | malc | audio_pcm_init_info (&hw->info, &obt_as); |
382 | b8e59f18 | malc | hw->samples = conf.samples; |
383 | b8e59f18 | malc | pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
|
384 | b8e59f18 | malc | if (!pa->pcm_buf) {
|
385 | b8e59f18 | malc | dolog ("Could not allocate buffer (%d bytes)\n",
|
386 | b8e59f18 | malc | hw->samples << hw->info.shift); |
387 | b8e59f18 | malc | goto fail2;
|
388 | b8e59f18 | malc | } |
389 | b8e59f18 | malc | |
390 | b8e59f18 | malc | if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
|
391 | b8e59f18 | malc | goto fail3;
|
392 | b8e59f18 | malc | } |
393 | b8e59f18 | malc | |
394 | b8e59f18 | malc | return 0; |
395 | b8e59f18 | malc | |
396 | b8e59f18 | malc | fail3:
|
397 | b8e59f18 | malc | free (pa->pcm_buf); |
398 | b8e59f18 | malc | pa->pcm_buf = NULL;
|
399 | b8e59f18 | malc | fail2:
|
400 | b8e59f18 | malc | pa_simple_free (pa->s); |
401 | b8e59f18 | malc | pa->s = NULL;
|
402 | b8e59f18 | malc | fail1:
|
403 | b8e59f18 | malc | return -1; |
404 | b8e59f18 | malc | } |
405 | b8e59f18 | malc | |
406 | b8e59f18 | malc | static void qpa_fini_out (HWVoiceOut *hw) |
407 | b8e59f18 | malc | { |
408 | b8e59f18 | malc | void *ret;
|
409 | b8e59f18 | malc | PAVoiceOut *pa = (PAVoiceOut *) hw; |
410 | b8e59f18 | malc | |
411 | b8e59f18 | malc | audio_pt_lock (&pa->pt, AUDIO_FUNC); |
412 | b8e59f18 | malc | pa->done = 1;
|
413 | b8e59f18 | malc | audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); |
414 | b8e59f18 | malc | audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); |
415 | b8e59f18 | malc | |
416 | b8e59f18 | malc | if (pa->s) {
|
417 | b8e59f18 | malc | pa_simple_free (pa->s); |
418 | b8e59f18 | malc | pa->s = NULL;
|
419 | b8e59f18 | malc | } |
420 | b8e59f18 | malc | |
421 | b8e59f18 | malc | audio_pt_fini (&pa->pt, AUDIO_FUNC); |
422 | b8e59f18 | malc | qemu_free (pa->pcm_buf); |
423 | b8e59f18 | malc | pa->pcm_buf = NULL;
|
424 | b8e59f18 | malc | } |
425 | b8e59f18 | malc | |
426 | b8e59f18 | malc | static void qpa_fini_in (HWVoiceIn *hw) |
427 | b8e59f18 | malc | { |
428 | b8e59f18 | malc | void *ret;
|
429 | b8e59f18 | malc | PAVoiceIn *pa = (PAVoiceIn *) hw; |
430 | b8e59f18 | malc | |
431 | b8e59f18 | malc | audio_pt_lock (&pa->pt, AUDIO_FUNC); |
432 | b8e59f18 | malc | pa->done = 1;
|
433 | b8e59f18 | malc | audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC); |
434 | b8e59f18 | malc | audio_pt_join (&pa->pt, &ret, AUDIO_FUNC); |
435 | b8e59f18 | malc | |
436 | b8e59f18 | malc | if (pa->s) {
|
437 | b8e59f18 | malc | pa_simple_free (pa->s); |
438 | b8e59f18 | malc | pa->s = NULL;
|
439 | b8e59f18 | malc | } |
440 | b8e59f18 | malc | |
441 | b8e59f18 | malc | audio_pt_fini (&pa->pt, AUDIO_FUNC); |
442 | b8e59f18 | malc | qemu_free (pa->pcm_buf); |
443 | b8e59f18 | malc | pa->pcm_buf = NULL;
|
444 | b8e59f18 | malc | } |
445 | b8e59f18 | malc | |
446 | b8e59f18 | malc | static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...) |
447 | b8e59f18 | malc | { |
448 | b8e59f18 | malc | (void) hw;
|
449 | b8e59f18 | malc | (void) cmd;
|
450 | b8e59f18 | malc | return 0; |
451 | b8e59f18 | malc | } |
452 | b8e59f18 | malc | |
453 | b8e59f18 | malc | static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) |
454 | b8e59f18 | malc | { |
455 | b8e59f18 | malc | (void) hw;
|
456 | b8e59f18 | malc | (void) cmd;
|
457 | b8e59f18 | malc | return 0; |
458 | b8e59f18 | malc | } |
459 | b8e59f18 | malc | |
460 | b8e59f18 | malc | /* common */
|
461 | b8e59f18 | malc | static void *qpa_audio_init (void) |
462 | b8e59f18 | malc | { |
463 | b8e59f18 | malc | return &conf;
|
464 | b8e59f18 | malc | } |
465 | b8e59f18 | malc | |
466 | b8e59f18 | malc | static void qpa_audio_fini (void *opaque) |
467 | b8e59f18 | malc | { |
468 | b8e59f18 | malc | (void) opaque;
|
469 | b8e59f18 | malc | } |
470 | b8e59f18 | malc | |
471 | b8e59f18 | malc | struct audio_option qpa_options[] = {
|
472 | b8e59f18 | malc | {"SAMPLES", AUD_OPT_INT, &conf.samples,
|
473 | b8e59f18 | malc | "buffer size in samples", NULL, 0}, |
474 | b8e59f18 | malc | |
475 | b8e59f18 | malc | {"DIVISOR", AUD_OPT_INT, &conf.divisor,
|
476 | b8e59f18 | malc | "threshold divisor", NULL, 0}, |
477 | b8e59f18 | malc | |
478 | b8e59f18 | malc | {"SERVER", AUD_OPT_STR, &conf.server,
|
479 | b8e59f18 | malc | "server address", NULL, 0}, |
480 | b8e59f18 | malc | |
481 | b8e59f18 | malc | {"SINK", AUD_OPT_STR, &conf.sink,
|
482 | b8e59f18 | malc | "sink device name", NULL, 0}, |
483 | b8e59f18 | malc | |
484 | b8e59f18 | malc | {"SOURCE", AUD_OPT_STR, &conf.source,
|
485 | b8e59f18 | malc | "source device name", NULL, 0}, |
486 | b8e59f18 | malc | |
487 | b8e59f18 | malc | {NULL, 0, NULL, NULL, NULL, 0} |
488 | b8e59f18 | malc | }; |
489 | b8e59f18 | malc | |
490 | b8e59f18 | malc | struct audio_pcm_ops qpa_pcm_ops = {
|
491 | b8e59f18 | malc | qpa_init_out, |
492 | b8e59f18 | malc | qpa_fini_out, |
493 | b8e59f18 | malc | qpa_run_out, |
494 | b8e59f18 | malc | qpa_write, |
495 | b8e59f18 | malc | qpa_ctl_out, |
496 | b8e59f18 | malc | qpa_init_in, |
497 | b8e59f18 | malc | qpa_fini_in, |
498 | b8e59f18 | malc | qpa_run_in, |
499 | b8e59f18 | malc | qpa_read, |
500 | b8e59f18 | malc | qpa_ctl_in |
501 | b8e59f18 | malc | }; |
502 | b8e59f18 | malc | |
503 | b8e59f18 | malc | struct audio_driver pa_audio_driver = {
|
504 | b8e59f18 | malc | INIT_FIELD (name = ) "pa",
|
505 | b8e59f18 | malc | INIT_FIELD (descr = ) "http://www.pulseaudio.org/",
|
506 | b8e59f18 | malc | INIT_FIELD (options = ) qpa_options, |
507 | b8e59f18 | malc | INIT_FIELD (init = ) qpa_audio_init, |
508 | b8e59f18 | malc | INIT_FIELD (fini = ) qpa_audio_fini, |
509 | b8e59f18 | malc | INIT_FIELD (pcm_ops = ) &qpa_pcm_ops, |
510 | b8e59f18 | malc | INIT_FIELD (can_be_default = ) 0,
|
511 | b8e59f18 | malc | INIT_FIELD (max_voices_out = ) INT_MAX, |
512 | b8e59f18 | malc | INIT_FIELD (max_voices_in = ) INT_MAX, |
513 | b8e59f18 | malc | INIT_FIELD (voice_size_out = ) sizeof (PAVoiceOut),
|
514 | b8e59f18 | malc | INIT_FIELD (voice_size_in = ) sizeof (PAVoiceIn)
|
515 | b8e59f18 | malc | }; |