VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/pulseaudio.c@ 22727

Last change on this file since 22727 was 22153, checked in by vboxsync, 15 years ago

pulse: fixed missing unlock when a server operation failed, for example due to a terminated server connection (fixes hangs during VM shutdown / savestate)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.1 KB
Line 
1/** @file
2 *
3 * VBox PulseAudio backend
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DEV_AUDIO
27#include <VBox/log.h>
28#include <iprt/mem.h>
29
30#include <pulse/pulseaudio.h>
31#include "pulse_stubs.h"
32
33#include "../../vl_vbox.h"
34#include "audio.h"
35#define AUDIO_CAP "pulse"
36#include "audio_int.h"
37#include <stdio.h>
38
39#define MAX_LOG_REL_ERRORS 32
40
41/*
42 * We use a g_pMainLoop in a separate thread g_pContext. We have to call functions for
43 * manipulating objects either from callback functions or we have to protect
44 * these functions by pa_threaded_mainloop_lock() / pa_threaded_mainloop_unlock().
45 */
46static struct pa_threaded_mainloop *g_pMainLoop;
47static struct pa_context *g_pContext;
48
49typedef struct PulseVoice
50{
51 HWVoiceOut hw;
52 void *pPCMBuf;
53 pa_stream *pStream;
54 int fOpSuccess;
55 unsigned cErrors;
56} PulseVoice;
57
58static struct
59{
60 int buffer_msecs_out;
61 int buffer_msecs_in;
62} conf
63=
64{
65 INIT_FIELD (.buffer_msecs_out = ) 100,
66 INIT_FIELD (.buffer_msecs_in = ) 100,
67};
68
69struct pulse_params_req
70{
71 int freq;
72 pa_sample_format_t pa_format;
73 int nchannels;
74};
75
76struct pulse_params_obt
77{
78 int freq;
79 pa_sample_format_t pa_format;
80 int nchannels;
81 unsigned long buffer_size;
82};
83
84static void pulse_check_fatal (PulseVoice *pulse, int rc)
85{
86 if (rc == PA_ERR_CONNECTIONTERMINATED)
87 {
88 /* XXX runtime warning */
89 LogRel(("Pulse: Audio input/output stopped!\n"));
90 pulse->cErrors = MAX_LOG_REL_ERRORS;
91 }
92}
93
94static pa_sample_format_t aud_to_pulsefmt (audfmt_e fmt)
95{
96 switch (fmt)
97 {
98 case AUD_FMT_U8:
99 return PA_SAMPLE_U8;
100
101 case AUD_FMT_S16:
102 return PA_SAMPLE_S16LE;
103
104#ifdef PA_SAMPLE_S32LE
105 case AUD_FMT_S32:
106 return PA_SAMPLE_S32LE;
107#endif
108
109 default:
110 dolog ("Bad audio format %d\n", fmt);
111 return PA_SAMPLE_U8;
112 }
113}
114
115
116static int pulse_to_audfmt (pa_sample_format_t pulsefmt, audfmt_e *fmt, int *endianess)
117{
118 switch (pulsefmt)
119 {
120 case PA_SAMPLE_U8:
121 *endianess = 0;
122 *fmt = AUD_FMT_U8;
123 break;
124
125 case PA_SAMPLE_S16LE:
126 *fmt = AUD_FMT_S16;
127 *endianess = 0;
128 break;
129
130 case PA_SAMPLE_S16BE:
131 *fmt = AUD_FMT_S16;
132 *endianess = 1;
133 break;
134
135#ifdef PA_SAMPLE_S32LE
136 case PA_SAMPLE_S32LE:
137 *fmt = AUD_FMT_S32;
138 *endianess = 0;
139 break;
140#endif
141
142#ifdef PA_SAMPLE_S32BE
143 case PA_SAMPLE_S32BE:
144 *fmt = AUD_FMT_S32;
145 *endianess = 1;
146 break;
147#endif
148
149 default:
150 return -1;
151 }
152 return 0;
153}
154
155static void context_state_callback(pa_context *c, void *userdata)
156{
157 switch (pa_context_get_state(c))
158 {
159 case PA_CONTEXT_READY:
160 case PA_CONTEXT_TERMINATED:
161 case PA_CONTEXT_FAILED:
162 pa_threaded_mainloop_signal(g_pMainLoop, 0);
163 break;
164 default:
165 break;
166 }
167}
168
169static void stream_state_callback(pa_stream *s, void *userdata)
170{
171 switch (pa_stream_get_state(s))
172 {
173 case PA_STREAM_READY:
174 case PA_STREAM_FAILED:
175 case PA_STREAM_TERMINATED:
176 pa_threaded_mainloop_signal(g_pMainLoop, 0);
177 break;
178 default:
179 break;
180 }
181}
182
183static void stream_latency_update_callback(pa_stream *s, void *userdata)
184{
185 pa_threaded_mainloop_signal(g_pMainLoop, 0);
186}
187
188static int pulse_open (int fIn, struct pulse_params_req *req,
189 struct pulse_params_obt *obt, pa_stream **ppStream)
190{
191 pa_sample_spec sspec;
192 pa_channel_map cmap;
193 pa_stream *pStream = NULL;
194 pa_buffer_attr bufAttr;
195 const pa_buffer_attr *pBufAttr;
196 const pa_sample_spec *pSampSpec;
197 char achPCMName[64];
198 pa_stream_flags_t flags;
199 int ms = fIn ? conf.buffer_msecs_in : conf.buffer_msecs_out;
200 const char *stream_name = audio_get_stream_name();
201
202 RTStrPrintf(achPCMName, sizeof(achPCMName), "%.32s%s%s%s",
203 stream_name ? stream_name : "",
204 stream_name ? " (" : "",
205 fIn ? "pcm_in" : "pcm_out",
206 stream_name ? ")" : "");
207 sspec.rate = req->freq;
208 sspec.channels = req->nchannels;
209 sspec.format = req->pa_format;
210
211 LogRel(("Pulse: open %s rate=%dHz channels=%d format=%s\n",
212 fIn ? "PCM_IN" : "PCM_OUT", req->freq, req->nchannels,
213 pa_sample_format_to_string(req->pa_format)));
214
215 if (!pa_sample_spec_valid(&sspec))
216 {
217 LogRel(("Pulse: Unsupported sample specification\n"));
218 goto fail;
219 }
220
221 pa_channel_map_init_auto(&cmap, sspec.channels, PA_CHANNEL_MAP_ALSA);
222
223#if 0
224 pa_cvolume_reset(&volume, sspec.channels);
225#endif
226
227 pa_threaded_mainloop_lock(g_pMainLoop);
228
229 if (!(pStream = pa_stream_new(g_pContext, achPCMName, &sspec, &cmap)))
230 {
231 LogRel(("Pulse: Cannot create stream %s\n", achPCMName));
232 goto unlock_and_fail;
233 }
234
235 pSampSpec = pa_stream_get_sample_spec(pStream);
236 obt->pa_format = pSampSpec->format;
237 obt->nchannels = pSampSpec->channels;
238 obt->freq = pSampSpec->rate;
239
240 pa_stream_set_state_callback(pStream, stream_state_callback, NULL);
241 pa_stream_set_latency_update_callback(pStream, stream_latency_update_callback, NULL);
242
243 memset(&bufAttr, 0, sizeof(bufAttr));
244 bufAttr.tlength = (pa_bytes_per_second(pSampSpec) * ms) / 1000;
245 bufAttr.maxlength = (bufAttr.tlength*3) / 2;
246 bufAttr.minreq = pa_bytes_per_second(pSampSpec) / 100; /* 10ms */
247 bufAttr.prebuf = bufAttr.tlength - bufAttr.minreq;
248 bufAttr.fragsize = pa_bytes_per_second(pSampSpec) / 100; /* 10ms */
249
250 flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
251 if (fIn)
252 {
253 if (pa_stream_connect_record(pStream, /*dev=*/NULL, &bufAttr, flags) < 0)
254 {
255 LogRel(("Pulse: Cannot connect record stream : %s\n",
256 pa_strerror(pa_context_errno(g_pContext))));
257 goto disconnect_unlock_and_fail;
258 }
259 }
260 else
261 {
262 if (pa_stream_connect_playback(pStream, /*dev=*/NULL, &bufAttr, flags,
263 NULL, NULL) < 0)
264 {
265 LogRel(("Pulse: Cannot connect playback stream: %s\n",
266 pa_strerror(pa_context_errno(g_pContext))));
267 goto disconnect_unlock_and_fail;
268 }
269 }
270
271 /* Wait until the stream is ready */
272 for (;;)
273 {
274 pa_stream_state_t sstate;
275 pa_threaded_mainloop_wait(g_pMainLoop);
276 sstate = pa_stream_get_state(pStream);
277 if (sstate == PA_STREAM_READY)
278 break;
279 else if (sstate == PA_STREAM_FAILED || sstate == PA_STREAM_TERMINATED)
280 {
281 LogRel(("Pulse: Failed to initialize stream (state %d)\n", sstate));
282 goto disconnect_unlock_and_fail;
283 }
284 }
285
286 pBufAttr = pa_stream_get_buffer_attr(pStream);
287 obt->buffer_size = pBufAttr->maxlength;
288
289 pa_threaded_mainloop_unlock(g_pMainLoop);
290
291 LogRel(("Pulse: buffer settings: max=%d tlength=%d prebuf=%d minreq=%d\n",
292 pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
293
294 *ppStream = pStream;
295 return 0;
296
297disconnect_unlock_and_fail:
298 pa_stream_disconnect(pStream);
299
300unlock_and_fail:
301 pa_threaded_mainloop_unlock(g_pMainLoop);
302
303fail:
304 if (pStream)
305 pa_stream_unref(pStream);
306
307 *ppStream = NULL;
308 return -1;
309}
310
311static int pulse_init_out (HWVoiceOut *hw, audsettings_t *as)
312{
313 PulseVoice *pulse = (PulseVoice *) hw;
314 struct pulse_params_req req;
315 struct pulse_params_obt obt;
316 audfmt_e effective_fmt;
317 int endianness;
318 audsettings_t obt_as;
319
320 req.pa_format = aud_to_pulsefmt (as->fmt);
321 req.freq = as->freq;
322 req.nchannels = as->nchannels;
323
324 if (pulse_open (/*fIn=*/0, &req, &obt, &pulse->pStream))
325 return -1;
326
327 if (pulse_to_audfmt (obt.pa_format, &effective_fmt, &endianness))
328 {
329 LogRel(("Pulse: Cannot find audio format %d\n", obt.pa_format));
330 return -1;
331 }
332
333 obt_as.freq = obt.freq;
334 obt_as.nchannels = obt.nchannels;
335 obt_as.fmt = effective_fmt;
336 obt_as.endianness = endianness;
337
338 audio_pcm_init_info (&hw->info, &obt_as);
339 hw->samples = obt.buffer_size >> hw->info.shift;
340
341 pulse->pPCMBuf = RTMemAllocZ(obt.buffer_size);
342 if (!pulse->pPCMBuf)
343 {
344 LogRel(("Pulse: Could not allocate DAC buffer of %d bytes\n", obt.buffer_size));
345 return -1;
346 }
347
348 return 0;
349}
350
351static void pulse_fini_out (HWVoiceOut *hw)
352{
353 PulseVoice *pulse = (PulseVoice *)hw;
354 if (pulse->pStream)
355 {
356 pa_stream_disconnect(pulse->pStream);
357 pa_stream_unref(pulse->pStream);
358 pulse->pStream = NULL;
359 }
360 if (pulse->pPCMBuf)
361 {
362 RTMemFree (pulse->pPCMBuf);
363 pulse->pPCMBuf = NULL;
364 }
365}
366
367static int pulse_run_out (HWVoiceOut *hw)
368{
369 PulseVoice *pulse = (PulseVoice *) hw;
370 int csLive, csDecr = 0, csSamples, csToWrite, csAvail;
371 size_t cbAvail, cbToWrite;
372 uint8_t *pu8Dst;
373 st_sample_t *psSrc;
374
375 csLive = audio_pcm_hw_get_live_out (hw);
376 if (!csLive)
377 return 0;
378
379 pa_threaded_mainloop_lock(g_pMainLoop);
380
381 cbAvail = pa_stream_writable_size (pulse->pStream);
382 if (cbAvail == (size_t)-1)
383 {
384 if (pulse->cErrors < MAX_LOG_REL_ERRORS)
385 {
386 int rc = pa_context_errno(g_pContext);
387 pulse->cErrors++;
388 LogRel(("Pulse: Failed to determine the writable size: %s\n",
389 pa_strerror(rc)));
390 pulse_check_fatal(pulse, rc);
391 }
392 goto unlock_and_exit;
393 }
394
395 csAvail = cbAvail >> hw->info.shift; /* bytes => samples */
396 csDecr = audio_MIN (csLive, csAvail);
397 csSamples = csDecr;
398
399 while (csSamples)
400 {
401 /* split request at the end of our samples buffer */
402 csToWrite = audio_MIN (csSamples, hw->samples - hw->rpos);
403 cbToWrite = csToWrite << hw->info.shift;
404 psSrc = hw->mix_buf + hw->rpos;
405 pu8Dst = advance (pulse->pPCMBuf, hw->rpos << hw->info.shift);
406
407 hw->clip (pu8Dst, psSrc, csToWrite);
408
409 if (pa_stream_write (pulse->pStream, pu8Dst, cbToWrite,
410 /*cleanup_callback=*/NULL, 0, PA_SEEK_RELATIVE) < 0)
411 {
412 LogRel(("Pulse: Failed to write %d samples: %s\n",
413 csToWrite, pa_strerror(pa_context_errno(g_pContext))));
414 break;
415 }
416 hw->rpos = (hw->rpos + csToWrite) % hw->samples;
417 csSamples -= csToWrite;
418 }
419
420unlock_and_exit:
421 pa_threaded_mainloop_unlock(g_pMainLoop);
422
423 return csDecr;
424}
425
426static int pulse_write (SWVoiceOut *sw, void *buf, int len)
427{
428 return audio_pcm_sw_write (sw, buf, len);
429}
430
431static void stream_success_callback(pa_stream *pStream, int success, void *userdata)
432{
433 PulseVoice *pulse = (PulseVoice *) userdata;
434 pulse->fOpSuccess = success;
435 pa_threaded_mainloop_signal(g_pMainLoop, 0);
436}
437
438typedef enum
439{
440 Unpause = 0,
441 Pause = 1,
442 Flush = 2,
443 Trigger = 3
444} pulse_cmd_t;
445
446static int pulse_ctrl (HWVoiceOut *hw, pulse_cmd_t cmd)
447{
448 PulseVoice *pulse = (PulseVoice *) hw;
449 pa_operation *op = NULL;
450
451 if (!pulse->pStream)
452 return 0;
453
454 pa_threaded_mainloop_lock(g_pMainLoop);
455 switch (cmd)
456 {
457 case Pause:
458 op = pa_stream_cork(pulse->pStream, 1, stream_success_callback, pulse);
459 break;
460 case Unpause:
461 op = pa_stream_cork(pulse->pStream, 0, stream_success_callback, pulse);
462 break;
463 case Flush:
464 op = pa_stream_flush(pulse->pStream, stream_success_callback, pulse);
465 break;
466 case Trigger:
467 op = pa_stream_trigger(pulse->pStream, stream_success_callback, pulse);
468 break;
469 default:
470 goto unlock_and_exit;
471 }
472 if (!op)
473 {
474 if (pulse->cErrors < MAX_LOG_REL_ERRORS)
475 {
476 int rc = pa_context_errno(g_pContext);
477 pulse->cErrors++;
478 LogRel(("Pulse: Failed ctrl cmd=%d to stream: %s\n",
479 cmd, pa_strerror(pa_context_errno(g_pContext))));
480 pulse_check_fatal(pulse, rc);
481 }
482 }
483 else
484 pa_operation_unref(op);
485
486unlock_and_exit:
487 pa_threaded_mainloop_unlock(g_pMainLoop);
488 return 0;
489}
490
491static int pulse_ctl_out (HWVoiceOut *hw, int cmd, ...)
492{
493 switch (cmd)
494 {
495 case VOICE_ENABLE:
496 pulse_ctrl(hw, Unpause);
497 pulse_ctrl(hw, Trigger);
498 break;
499 case VOICE_DISABLE:
500 pulse_ctrl(hw, Flush);
501 break;
502 default:
503 return -1;
504 }
505 return 0;
506}
507
508static int pulse_init_in (HWVoiceIn *hw, audsettings_t *as)
509{
510 PulseVoice *pulse = (PulseVoice *) hw;
511 struct pulse_params_req req;
512 struct pulse_params_obt obt;
513 audfmt_e effective_fmt;
514 int endianness;
515 audsettings_t obt_as;
516
517 req.pa_format = aud_to_pulsefmt (as->fmt);
518 req.freq = as->freq;
519 req.nchannels = as->nchannels;
520
521 if (pulse_open (/*fIn=*/1, &req, &obt, &pulse->pStream))
522 return -1;
523
524 if (pulse_to_audfmt (obt.pa_format, &effective_fmt, &endianness))
525 {
526 LogRel(("Pulse: Cannot find audio format %d\n", obt.pa_format));
527 return -1;
528 }
529
530 obt_as.freq = obt.freq;
531 obt_as.nchannels = obt.nchannels;
532 obt_as.fmt = effective_fmt;
533 obt_as.endianness = endianness;
534
535 audio_pcm_init_info (&hw->info, &obt_as);
536
537 /* pcm_in: reserve twice as the maximum buffer length because of peek()/drop(). */
538 hw->samples = 2 * (obt.buffer_size >> hw->info.shift);
539
540 /* no buffer for input */
541 pulse->pPCMBuf = NULL;
542
543 return 0;
544}
545
546static void pulse_fini_in (HWVoiceIn *hw)
547{
548 PulseVoice *pulse = (PulseVoice *)hw;
549 if (pulse->pStream)
550 {
551 pa_stream_disconnect(pulse->pStream);
552 pa_stream_unref(pulse->pStream);
553 pulse->pStream = NULL;
554 }
555 if (pulse->pPCMBuf)
556 {
557 RTMemFree (pulse->pPCMBuf);
558 pulse->pPCMBuf = NULL;
559 }
560}
561
562static int pulse_run_in (HWVoiceIn *hw)
563{
564 PulseVoice *pulse = (PulseVoice *) hw;
565 int csDead, csDecr = 0, csSamples, csRead, csAvail;
566 size_t cbAvail;
567 const void *pu8Src;
568 st_sample_t *psDst;
569
570 csDead = hw->samples - audio_pcm_hw_get_live_in (hw);
571
572 if (!csDead)
573 return 0; /* no buffer available */
574
575 pa_threaded_mainloop_lock(g_pMainLoop);
576
577 if (pa_stream_peek(pulse->pStream, &pu8Src, &cbAvail) < 0)
578 {
579 LogRel(("Pulse: Peek failed: %s\n",
580 pa_strerror(pa_context_errno(g_pContext))));
581 goto unlock_and_exit;
582 }
583 if (!pu8Src)
584 goto unlock_and_exit;
585
586 csAvail = cbAvail >> hw->info.shift;
587 csDecr = audio_MIN (csDead, csAvail);
588
589 csSamples = csDecr;
590
591 while (csSamples)
592 {
593 /* split request at the end of our samples buffer */
594 psDst = hw->conv_buf + hw->wpos;
595 csRead = audio_MIN (csSamples, hw->samples - hw->wpos);
596 hw->conv (psDst, pu8Src, csRead, &nominal_volume);
597 hw->wpos = (hw->wpos + csRead) % hw->samples;
598 csSamples -= csRead;
599 pu8Src = (const void*)((uint8_t*)pu8Src + (csRead << hw->info.shift));
600 }
601
602 pa_stream_drop(pulse->pStream);
603
604unlock_and_exit:
605 pa_threaded_mainloop_unlock(g_pMainLoop);
606
607 return csDecr;
608}
609
610static int pulse_read (SWVoiceIn *sw, void *buf, int size)
611{
612 return audio_pcm_sw_read (sw, buf, size);
613}
614
615static int pulse_ctl_in (HWVoiceIn *hw, int cmd, ...)
616{
617 return 0;
618}
619
620static void *pulse_audio_init (void)
621{
622 int rc;
623
624 rc = audioLoadPulseLib();
625 if (RT_FAILURE(rc))
626 {
627 LogRel(("Pulse: Failed to load the PulseAudio shared library! Error %Rrc\n", rc));
628 return NULL;
629 }
630 if (!(g_pMainLoop = pa_threaded_mainloop_new()))
631 {
632 LogRel(("Pulse: Failed to allocate main loop: %s\n",
633 pa_strerror(pa_context_errno(g_pContext))));
634 goto fail;
635 }
636 if (!(g_pContext = pa_context_new(pa_threaded_mainloop_get_api(g_pMainLoop), "VBox")))
637 {
638 LogRel(("Pulse: Failed to allocate context: %s\n",
639 pa_strerror(pa_context_errno(g_pContext))));
640 goto fail;
641 }
642 if (pa_threaded_mainloop_start(g_pMainLoop) < 0)
643 {
644 LogRel(("Pulse: Failed to start threaded mainloop: %s\n",
645 pa_strerror(pa_context_errno(g_pContext))));
646 goto fail;
647 }
648
649 pa_context_set_state_callback(g_pContext, context_state_callback, NULL);
650 pa_threaded_mainloop_lock(g_pMainLoop);
651
652 if (pa_context_connect(g_pContext, /*server=*/NULL, 0, NULL) < 0)
653 {
654 LogRel(("Pulse: Failed to connect to server: %s\n",
655 pa_strerror(pa_context_errno(g_pContext))));
656 goto unlock_and_fail;
657 }
658
659 /* Wait until the g_pContext is ready */
660 for (;;)
661 {
662 pa_context_state_t cstate;
663 pa_threaded_mainloop_wait(g_pMainLoop);
664 cstate = pa_context_get_state(g_pContext);
665 if (cstate == PA_CONTEXT_READY)
666 break;
667 else if (cstate == PA_CONTEXT_TERMINATED || cstate == PA_CONTEXT_FAILED)
668 {
669 LogRel(("Pulse: Failed to initialize context (state %d)\n", cstate));
670 goto unlock_and_fail;
671 }
672 }
673 pa_threaded_mainloop_unlock(g_pMainLoop);
674
675 return &conf;
676
677unlock_and_fail:
678 if (g_pMainLoop)
679 pa_threaded_mainloop_unlock(g_pMainLoop);
680
681fail:
682 if (g_pMainLoop)
683 pa_threaded_mainloop_stop(g_pMainLoop);
684
685 if (g_pContext)
686 {
687 pa_context_disconnect(g_pContext);
688 pa_context_unref(g_pContext);
689 g_pContext = NULL;
690 }
691 if (g_pMainLoop)
692 {
693 pa_threaded_mainloop_free(g_pMainLoop);
694 g_pMainLoop = NULL;
695 }
696 return NULL;
697}
698
699static void pulse_audio_fini (void *opaque)
700{
701 if (g_pMainLoop)
702 pa_threaded_mainloop_stop(g_pMainLoop);
703 if (g_pContext)
704 {
705 pa_context_disconnect(g_pContext);
706 pa_context_unref(g_pContext);
707 g_pContext = NULL;
708 }
709 if (g_pMainLoop)
710 {
711 pa_threaded_mainloop_free(g_pMainLoop);
712 g_pMainLoop = NULL;
713 }
714 (void) opaque;
715}
716
717static struct audio_option pulse_options[] =
718{
719 {"DAC_MS", AUD_OPT_INT, &conf.buffer_msecs_out,
720 "DAC period size in milliseconds", NULL, 0},
721 {"ADC_MS", AUD_OPT_INT, &conf.buffer_msecs_in,
722 "ADC period size in milliseconds", NULL, 0},
723 {NULL, 0, NULL, NULL, NULL, 0}
724};
725
726static struct audio_pcm_ops pulse_pcm_ops =
727{
728 pulse_init_out,
729 pulse_fini_out,
730 pulse_run_out,
731 pulse_write,
732 pulse_ctl_out,
733
734 pulse_init_in,
735 pulse_fini_in,
736 pulse_run_in,
737 pulse_read,
738 pulse_ctl_in
739};
740
741struct audio_driver pulse_audio_driver =
742{
743 INIT_FIELD (name = ) "pulse",
744 INIT_FIELD (descr = ) "PulseAudio http://www.pulseaudio.org",
745 INIT_FIELD (options = ) pulse_options,
746 INIT_FIELD (init = ) pulse_audio_init,
747 INIT_FIELD (fini = ) pulse_audio_fini,
748 INIT_FIELD (pcm_ops = ) &pulse_pcm_ops,
749 INIT_FIELD (can_be_default = ) 1,
750 INIT_FIELD (max_voices_out = ) INT_MAX,
751 INIT_FIELD (max_voices_in = ) INT_MAX,
752 INIT_FIELD (voice_size_out = ) sizeof (PulseVoice),
753 INIT_FIELD (voice_size_in = ) sizeof (PulseVoice)
754};
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette