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