VirtualBox

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

Last change on this file since 17225 was 15783, checked in by vboxsync, 16 years ago

pulse: limit the number of release warnings

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