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