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