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