VirtualBox

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

Last change on this file since 27031 was 26783, checked in by vboxsync, 15 years ago

pulse: fixed regression with temporarily freezing guests if the guest pauses the AC97 controller

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.3 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 /** not accessed from within this context */
52 union
53 {
54 HWVoiceOut In;
55 HWVoiceIn Out;
56 } hw;
57 /** DAC buffer */
58 void *pPCMBuf;
59 /** Pulse stream */
60 pa_stream *pStream;
61 /** Pulse sample format and attribute specification */
62 pa_sample_spec SampleSpec;
63 /** Pulse playback and buffer metrics */
64 pa_buffer_attr BufAttr;
65 int fOpSuccess;
66 /** number of logged errors */
67 unsigned cErrors;
68 /** Pulse record peek buffer */
69 const uint8_t *pu8PeekBuf;
70 size_t cbPeekBuf;
71 size_t offPeekBuf;
72} PulseVoice;
73
74/* The desired buffer length in milliseconds. Will be the target total stream
75 * latency on newer version of pulse. Apparent latency can be less (or more.)
76 */
77static struct
78{
79 int buffer_msecs_out;
80 int buffer_msecs_in;
81} conf
82=
83{
84 INIT_FIELD (.buffer_msecs_out = ) 100,
85 INIT_FIELD (.buffer_msecs_in = ) 100,
86};
87
88static pa_sample_format_t aud_to_pulsefmt (audfmt_e fmt)
89{
90 switch (fmt)
91 {
92 case AUD_FMT_U8:
93 return PA_SAMPLE_U8;
94
95 case AUD_FMT_S16:
96 return PA_SAMPLE_S16LE;
97
98#ifdef PA_SAMPLE_S32LE
99 case AUD_FMT_S32:
100 return PA_SAMPLE_S32LE;
101#endif
102
103 default:
104 dolog ("Bad audio format %d\n", fmt);
105 return PA_SAMPLE_U8;
106 }
107}
108
109
110static int pulse_to_audfmt (pa_sample_format_t pulsefmt, audfmt_e *fmt, int *endianess)
111{
112 switch (pulsefmt)
113 {
114 case PA_SAMPLE_U8:
115 *endianess = 0;
116 *fmt = AUD_FMT_U8;
117 break;
118
119 case PA_SAMPLE_S16LE:
120 *fmt = AUD_FMT_S16;
121 *endianess = 0;
122 break;
123
124 case PA_SAMPLE_S16BE:
125 *fmt = AUD_FMT_S16;
126 *endianess = 1;
127 break;
128
129#ifdef PA_SAMPLE_S32LE
130 case PA_SAMPLE_S32LE:
131 *fmt = AUD_FMT_S32;
132 *endianess = 0;
133 break;
134#endif
135
136#ifdef PA_SAMPLE_S32BE
137 case PA_SAMPLE_S32BE:
138 *fmt = AUD_FMT_S32;
139 *endianess = 1;
140 break;
141#endif
142
143 default:
144 return -1;
145 }
146 return 0;
147}
148
149static void context_state_callback(pa_context *c, void *userdata)
150{
151 PulseVoice *pPulse = (PulseVoice *)userdata;
152 switch (pa_context_get_state(c))
153 {
154 case PA_CONTEXT_READY:
155 case PA_CONTEXT_TERMINATED:
156 pa_threaded_mainloop_signal(g_pMainLoop, 0);
157 break;
158
159 case PA_CONTEXT_FAILED:
160 LogRel(("Pulse: Audio input/output stopped!\n"));
161 if (pPulse)
162 pPulse->cErrors = MAX_LOG_REL_ERRORS;
163 pa_threaded_mainloop_signal(g_pMainLoop, 0);
164 break;
165
166 default:
167 break;
168 }
169}
170
171static void stream_state_callback(pa_stream *s, void *userdata)
172{
173 switch (pa_stream_get_state(s))
174 {
175 case PA_STREAM_READY:
176 case PA_STREAM_FAILED:
177 case PA_STREAM_TERMINATED:
178 pa_threaded_mainloop_signal(g_pMainLoop, 0);
179 break;
180
181 default:
182 break;
183 }
184}
185
186static int pulse_open (int fIn, pa_stream **ppStream, pa_sample_spec *pSampleSpec,
187 pa_buffer_attr *pBufAttr)
188{
189 const pa_buffer_attr *pBufAttrObtained;
190 pa_stream *pStream = NULL;
191 char achPCMName[64];
192 pa_stream_flags_t flags = 0;
193 const char *stream_name = audio_get_stream_name();
194
195 RTStrPrintf(achPCMName, sizeof(achPCMName), "%.32s%s%s%s",
196 stream_name ? stream_name : "",
197 stream_name ? " (" : "",
198 fIn ? "pcm_in" : "pcm_out",
199 stream_name ? ")" : "");
200
201 LogRel(("Pulse: open %s rate=%dHz channels=%d format=%s\n",
202 fIn ? "PCM_IN" : "PCM_OUT", pSampleSpec->rate, pSampleSpec->channels,
203 pa_sample_format_to_string(pSampleSpec->format)));
204
205 if (!pa_sample_spec_valid(pSampleSpec))
206 {
207 LogRel(("Pulse: Unsupported sample specification\n"));
208 goto fail;
209 }
210
211 pa_threaded_mainloop_lock(g_pMainLoop);
212
213 if (!(pStream = pa_stream_new(g_pContext, achPCMName, pSampleSpec, /*channel_map=*/NULL)))
214 {
215 LogRel(("Pulse: Cannot create stream %s\n", achPCMName));
216 goto unlock_and_fail;
217 }
218
219 pa_stream_set_state_callback(pStream, stream_state_callback, NULL);
220
221#if PA_API_VERSION >= 12
222 /* XXX */
223 flags |= PA_STREAM_ADJUST_LATENCY;
224#endif
225
226#if 0
227 /* not applicable as we don't use pa_stream_get_latency() and pa_stream_get_time() */
228 flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
229#endif
230
231 if (fIn)
232 {
233 LogRel(("Pulse: Requested record buffer attributes: maxlength=%d fragsize=%d\n",
234 pBufAttr->maxlength, pBufAttr->fragsize));
235
236 if (pa_stream_connect_record(pStream, /*dev=*/NULL, pBufAttr, flags) < 0)
237 {
238 LogRel(("Pulse: Cannot connect record stream: %s\n",
239 pa_strerror(pa_context_errno(g_pContext))));
240 goto disconnect_unlock_and_fail;
241 }
242 }
243 else
244 {
245 LogRel(("Pulse: Requested playback buffer attributes: maxlength=%d tlength=%d prebuf=%d minreq=%d\n",
246 pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
247
248 flags |= PA_STREAM_START_CORKED;
249
250 if (pa_stream_connect_playback(pStream, /*dev=*/NULL, pBufAttr, flags,
251 /*cvolume=*/NULL, /*sync_stream=*/NULL) < 0)
252 {
253 LogRel(("Pulse: Cannot connect playback stream: %s\n",
254 pa_strerror(pa_context_errno(g_pContext))));
255 goto disconnect_unlock_and_fail;
256 }
257 }
258
259 /* Wait until the stream is ready */
260 for (;;)
261 {
262 pa_stream_state_t sstate;
263 pa_threaded_mainloop_wait(g_pMainLoop);
264
265 sstate = pa_stream_get_state(pStream);
266 if (sstate == PA_STREAM_READY)
267 break;
268 else if (sstate == PA_STREAM_FAILED || sstate == PA_STREAM_TERMINATED)
269 {
270 LogRel(("Pulse: Failed to initialize stream (state %d)\n", sstate));
271 goto disconnect_unlock_and_fail;
272 }
273 }
274
275 pBufAttrObtained = pa_stream_get_buffer_attr(pStream);
276 memcpy(pBufAttr, pBufAttrObtained, sizeof(pa_buffer_attr));
277
278 if (fIn)
279 {
280 LogRel(("Pulse: Obtained record buffer attributes: maxlength=%d fragsize=%d\n",
281 pBufAttr->maxlength, pBufAttr->fragsize));
282 }
283 else
284 {
285 LogRel(("Pulse: Obtained playback buffer attributes: maxlength=%d tlength=%d prebuf=%d minreq=%d\n",
286 pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
287 }
288
289 pa_threaded_mainloop_unlock(g_pMainLoop);
290 *ppStream = pStream;
291 return 0;
292
293disconnect_unlock_and_fail:
294 pa_stream_disconnect(pStream);
295
296unlock_and_fail:
297 pa_threaded_mainloop_unlock(g_pMainLoop);
298
299fail:
300 if (pStream)
301 pa_stream_unref(pStream);
302
303 *ppStream = NULL;
304 return -1;
305}
306
307static int pulse_init_out (HWVoiceOut *hw, audsettings_t *as)
308{
309 PulseVoice *pPulse = (PulseVoice *) hw;
310 audsettings_t obt_as;
311 int cbBuf;
312
313 pPulse->SampleSpec.format = aud_to_pulsefmt (as->fmt);
314 pPulse->SampleSpec.rate = as->freq;
315 pPulse->SampleSpec.channels = as->nchannels;
316
317 /* Note that setting maxlength to -1 does not work on PulseAudio servers
318 * older than 0.9.10. So use the suggested value of 3/2 of tlength */
319 pPulse->BufAttr.tlength = (pa_bytes_per_second(&pPulse->SampleSpec)
320 * conf.buffer_msecs_out) / 1000;
321 pPulse->BufAttr.maxlength = (pPulse->BufAttr.tlength * 3) / 2;
322 pPulse->BufAttr.prebuf = -1; /* Same as tlength */
323 pPulse->BufAttr.minreq = -1; /* Pulse should set something sensible for minreq on it's own */
324
325 /* Notice that the struct BufAttr is updated to the obtained values after this call */
326 if (pulse_open (0, &pPulse->pStream, &pPulse->SampleSpec, &pPulse->BufAttr))
327 return -1;
328
329 if (pulse_to_audfmt (pPulse->SampleSpec.format, &obt_as.fmt, &obt_as.endianness))
330 {
331 LogRel(("Pulse: Cannot find audio format %d\n", pPulse->SampleSpec.format));
332 return -1;
333 }
334
335 obt_as.freq = pPulse->SampleSpec.rate;
336 obt_as.nchannels = pPulse->SampleSpec.channels;
337
338 audio_pcm_init_info (&hw->info, &obt_as);
339 cbBuf = audio_MIN(pPulse->BufAttr.tlength * 2, pPulse->BufAttr.maxlength);
340
341 pPulse->pPCMBuf = RTMemAllocZ(cbBuf);
342 if (!pPulse->pPCMBuf)
343 {
344 LogRel(("Pulse: Could not allocate DAC buffer of %d bytes\n", cbBuf));
345 return -1;
346 }
347
348 /* Convert from bytes to frames (aka samples) */
349 hw->samples = cbBuf >> hw->info.shift;
350
351 return 0;
352}
353
354static void pulse_fini_out (HWVoiceOut *hw)
355{
356 PulseVoice *pPulse = (PulseVoice *)hw;
357
358 if (pPulse->pStream)
359 {
360 pa_threaded_mainloop_lock(g_pMainLoop);
361 pa_stream_disconnect(pPulse->pStream);
362 pa_stream_unref(pPulse->pStream);
363 pa_threaded_mainloop_unlock(g_pMainLoop);
364 pPulse->pStream = NULL;
365 }
366
367 if (pPulse->pPCMBuf)
368 {
369 RTMemFree (pPulse->pPCMBuf);
370 pPulse->pPCMBuf = NULL;
371 }
372}
373
374static int pulse_run_out (HWVoiceOut *hw)
375{
376 PulseVoice *pPulse = (PulseVoice *) hw;
377 int cFramesLive;
378 int cFramesWritten = 0;
379 int csSamples;
380 int cFramesToWrite;
381 int cFramesAvail;
382 size_t cbAvail;
383 size_t cbToWrite;
384 uint8_t *pu8Dst;
385 st_sample_t *psSrc;
386
387 cFramesLive = audio_pcm_hw_get_live_out (hw);
388 if (!cFramesLive)
389 return 0;
390
391 pa_threaded_mainloop_lock(g_pMainLoop);
392
393 cbAvail = pa_stream_writable_size (pPulse->pStream);
394 if (cbAvail == (size_t)-1)
395 {
396 if (pPulse->cErrors < MAX_LOG_REL_ERRORS)
397 {
398 int rc = pa_context_errno(g_pContext);
399 pPulse->cErrors++;
400 LogRel(("Pulse: Failed to determine the writable size: %s\n",
401 pa_strerror(rc)));
402 }
403 goto unlock_and_exit;
404 }
405
406 cFramesAvail = cbAvail >> hw->info.shift; /* bytes => samples */
407 cFramesWritten = audio_MIN (cFramesLive, cFramesAvail);
408 csSamples = cFramesWritten;
409
410 while (csSamples)
411 {
412 /* split request at the end of our samples buffer */
413 cFramesToWrite = audio_MIN (csSamples, hw->samples - hw->rpos);
414 cbToWrite = cFramesToWrite << hw->info.shift;
415 psSrc = hw->mix_buf + hw->rpos;
416 pu8Dst = advance (pPulse->pPCMBuf, hw->rpos << hw->info.shift);
417
418 hw->clip (pu8Dst, psSrc, cFramesToWrite);
419
420 if (pa_stream_write (pPulse->pStream, pu8Dst, cbToWrite,
421 /*cleanup_callback=*/NULL, 0, PA_SEEK_RELATIVE) < 0)
422 {
423 LogRel(("Pulse: Failed to write %d samples: %s\n",
424 cFramesToWrite, pa_strerror(pa_context_errno(g_pContext))));
425 break;
426 }
427 hw->rpos = (hw->rpos + cFramesToWrite) % hw->samples;
428 csSamples -= cFramesToWrite;
429 }
430
431unlock_and_exit:
432 pa_threaded_mainloop_unlock(g_pMainLoop);
433
434 return cFramesWritten;
435}
436
437static int pulse_write (SWVoiceOut *sw, void *buf, int len)
438{
439 return audio_pcm_sw_write (sw, buf, len);
440}
441
442static void stream_success_callback(pa_stream *pStream, int success, void *userdata)
443{
444 PulseVoice *pPulse = (PulseVoice *) userdata;
445 pPulse->fOpSuccess = success;
446 if (!success)
447 {
448 if (pPulse->cErrors < MAX_LOG_REL_ERRORS)
449 {
450 int rc = pa_context_errno(g_pContext);
451 pPulse->cErrors++;
452 LogRel(("Pulse: Failed stream operation: %s\n", pa_strerror(rc)));
453 }
454 }
455 pa_threaded_mainloop_signal(g_pMainLoop, 0);
456}
457
458static int pulse_wait_for_operation (pa_operation *op)
459{
460 if (op)
461 {
462 while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
463 pa_threaded_mainloop_wait(g_pMainLoop);
464 pa_operation_unref(op);
465 }
466
467 return 1;
468}
469
470static int pulse_ctl_out (HWVoiceOut *hw, int cmd, ...)
471{
472 PulseVoice *pPulse = (PulseVoice *) hw;
473
474 switch (cmd)
475 {
476 case VOICE_ENABLE:
477 /* Start audio output. */
478 pa_threaded_mainloop_lock(g_pMainLoop);
479 pulse_wait_for_operation(pa_stream_cork(pPulse->pStream, 0,
480 stream_success_callback, pPulse));
481 pa_threaded_mainloop_unlock(g_pMainLoop);
482 break;
483
484 case VOICE_DISABLE:
485 /* Pause audio output. Note that we must return immediately from here
486 * so waiting until the buffers are flushed (trigger+drain) is not an
487 * option! It could be sufficient to cork the audio stream (we are
488 * called if the Pause bit of the AC97 x_CR register is set) but ALSA
489 * uses snd_pcm_drop() dropping all pending frames so we do the same
490 * here. */
491 pa_threaded_mainloop_lock(g_pMainLoop);
492 pulse_wait_for_operation(pa_stream_flush(pPulse->pStream,
493 stream_success_callback, pPulse));
494 pulse_wait_for_operation(pa_stream_cork(pPulse->pStream, 1,
495 stream_success_callback, pPulse));
496 pa_threaded_mainloop_unlock(g_pMainLoop);
497 break;
498
499 default:
500 return -1;
501 }
502 return 0;
503}
504
505static int pulse_init_in (HWVoiceIn *hw, audsettings_t *as)
506{
507 PulseVoice *pPulse = (PulseVoice *) hw;
508 audsettings_t obt_as;
509
510 pPulse->SampleSpec.format = aud_to_pulsefmt (as->fmt);
511 pPulse->SampleSpec.rate = as->freq;
512 pPulse->SampleSpec.channels = as->nchannels;
513
514 /* XXX check these values */
515 pPulse->BufAttr.fragsize = (pa_bytes_per_second(&pPulse->SampleSpec)
516 * conf.buffer_msecs_in) / 1000;
517 pPulse->BufAttr.maxlength = (pPulse->BufAttr.fragsize * 3) / 2;
518 /* Other memebers of pa_buffer_attr are ignored for record streams */
519
520 if (pulse_open (1, &pPulse->pStream, &pPulse->SampleSpec, &pPulse->BufAttr))
521 return -1;
522
523 if (pulse_to_audfmt (pPulse->SampleSpec.format, &obt_as.fmt, &obt_as.endianness))
524 {
525 LogRel(("Pulse: Cannot find audio format %d\n", pPulse->SampleSpec.format));
526 return -1;
527 }
528
529 obt_as.freq = pPulse->SampleSpec.rate;
530 obt_as.nchannels = pPulse->SampleSpec.channels;
531 audio_pcm_init_info (&hw->info, &obt_as);
532 hw->samples = audio_MIN(pPulse->BufAttr.fragsize * 10, pPulse->BufAttr.maxlength)
533 >> hw->info.shift;
534 pPulse->pu8PeekBuf = NULL;
535
536 return 0;
537}
538
539static void pulse_fini_in (HWVoiceIn *hw)
540{
541 PulseVoice *pPulse = (PulseVoice *)hw;
542
543 if (pPulse->pStream)
544 {
545 pa_threaded_mainloop_lock(g_pMainLoop);
546 pa_stream_disconnect(pPulse->pStream);
547 pa_stream_unref(pPulse->pStream);
548 pa_threaded_mainloop_unlock(g_pMainLoop);
549 pPulse->pStream = NULL;
550 }
551}
552
553static int pulse_run_in (HWVoiceIn *hw)
554{
555 PulseVoice *pPulse = (PulseVoice *) hw;
556 const int hwshift = hw->info.shift;
557 int cFramesRead = 0; /* total frames which have been read this call */
558 int cFramesAvail; /* total frames available from pulse at start of call */
559 int cFramesToRead; /* the largest amount we want/can get this call */
560 int cFramesToPeek; /* the largest amount we want/can get this peek */
561
562 /* We should only call pa_stream_readable_size() once and trust the first value */
563 pa_threaded_mainloop_lock(g_pMainLoop);
564 cFramesAvail = pa_stream_readable_size(pPulse->pStream) >> hwshift;
565 pa_threaded_mainloop_unlock(g_pMainLoop);
566
567 if (cFramesAvail == -1)
568 {
569 if (pPulse->cErrors < MAX_LOG_REL_ERRORS)
570 {
571 int rc = pa_context_errno(g_pContext);
572 pPulse->cErrors++;
573 LogRel(("Pulse: Failed to determine the readable size: %s\n",
574 pa_strerror(rc)));
575 }
576 return 0;
577 }
578
579 /* If the buffer was not dropped last call, add what remains */
580 if (pPulse->pu8PeekBuf)
581 cFramesAvail += (pPulse->cbPeekBuf - pPulse->offPeekBuf) >> hwshift;
582
583 cFramesToRead = audio_MIN(cFramesAvail, hw->samples - audio_pcm_hw_get_live_in(hw));
584 for (; cFramesToRead; cFramesToRead -= cFramesToPeek)
585 {
586 /* If there is no data, do another peek */
587 if (!pPulse->pu8PeekBuf)
588 {
589 pa_threaded_mainloop_lock(g_pMainLoop);
590 pa_stream_peek(pPulse->pStream, (const void**)&pPulse->pu8PeekBuf, &pPulse->cbPeekBuf);
591 pa_threaded_mainloop_unlock(g_pMainLoop);
592 pPulse->offPeekBuf = 0;
593 if ( !pPulse->pu8PeekBuf
594 || !pPulse->cbPeekBuf)
595 break;
596 }
597
598 cFramesToPeek = audio_MIN((signed)( pPulse->cbPeekBuf
599 - pPulse->offPeekBuf) >> hwshift,
600 cFramesToRead);
601
602 /* Check for wrapping around the buffer end */
603 if (hw->wpos + cFramesToPeek > hw->samples)
604 {
605 int cFramesDelta = hw->samples - hw->wpos;
606
607 hw->conv(hw->conv_buf + hw->wpos,
608 pPulse->pu8PeekBuf + pPulse->offPeekBuf,
609 cFramesDelta,
610 &nominal_volume);
611
612 hw->conv(hw->conv_buf,
613 pPulse->pu8PeekBuf + pPulse->offPeekBuf + (cFramesDelta << hwshift),
614 cFramesToPeek - cFramesDelta,
615 &nominal_volume);
616 }
617 else
618 {
619 hw->conv(hw->conv_buf + hw->wpos,
620 pPulse->pu8PeekBuf + pPulse->offPeekBuf,
621 cFramesToPeek,
622 &nominal_volume);
623 }
624
625 cFramesRead += cFramesToPeek;
626 hw->wpos = (hw->wpos + cFramesToPeek) % hw->samples;
627 pPulse->offPeekBuf += cFramesToPeek << hwshift;
628
629 /* If the buffer is done, drop it */
630 if (pPulse->offPeekBuf == pPulse->cbPeekBuf)
631 {
632 pa_threaded_mainloop_lock(g_pMainLoop);
633 pa_stream_drop(pPulse->pStream);
634 pa_threaded_mainloop_unlock(g_pMainLoop);
635 pPulse->pu8PeekBuf = NULL;
636 }
637 }
638
639 return cFramesRead;
640}
641
642static int pulse_read (SWVoiceIn *sw, void *buf, int size)
643{
644 return audio_pcm_sw_read (sw, buf, size);
645}
646
647static int pulse_ctl_in (HWVoiceIn *hw, int cmd, ...)
648{
649 PulseVoice *pPulse = (PulseVoice *)hw;
650
651 switch (cmd)
652 {
653 case VOICE_ENABLE:
654 pa_threaded_mainloop_lock(g_pMainLoop);
655 pulse_wait_for_operation(pa_stream_cork(pPulse->pStream, 0,
656 stream_success_callback, pPulse));
657 pa_threaded_mainloop_unlock(g_pMainLoop);
658 break;
659
660 case VOICE_DISABLE:
661 pa_threaded_mainloop_lock(g_pMainLoop);
662 if (pPulse->pu8PeekBuf)
663 {
664 pa_stream_drop(pPulse->pStream);
665 pPulse->pu8PeekBuf = NULL;
666 }
667 pulse_wait_for_operation(pa_stream_cork(pPulse->pStream, 1,
668 stream_success_callback, pPulse));
669 pa_threaded_mainloop_unlock(g_pMainLoop);
670 break;
671
672 default:
673 return -1;
674 }
675 return 0;
676}
677
678static void *pulse_audio_init (void)
679{
680 int rc;
681
682 rc = audioLoadPulseLib();
683 if (RT_FAILURE(rc))
684 {
685 LogRel(("Pulse: Failed to load the PulseAudio shared library! Error %Rrc\n", rc));
686 return NULL;
687 }
688
689 if (!(g_pMainLoop = pa_threaded_mainloop_new()))
690 {
691 LogRel(("Pulse: Failed to allocate main loop: %s\n",
692 pa_strerror(pa_context_errno(g_pContext))));
693 goto fail;
694 }
695
696 if (!(g_pContext = pa_context_new(pa_threaded_mainloop_get_api(g_pMainLoop), "VBox")))
697 {
698 LogRel(("Pulse: Failed to allocate context: %s\n",
699 pa_strerror(pa_context_errno(g_pContext))));
700 goto fail;
701 }
702
703 if (pa_threaded_mainloop_start(g_pMainLoop) < 0)
704 {
705 LogRel(("Pulse: Failed to start threaded mainloop: %s\n",
706 pa_strerror(pa_context_errno(g_pContext))));
707 goto fail;
708 }
709
710 pa_context_set_state_callback(g_pContext, context_state_callback, NULL);
711 pa_threaded_mainloop_lock(g_pMainLoop);
712
713 if (pa_context_connect(g_pContext, /*server=*/NULL, 0, NULL) < 0)
714 {
715 LogRel(("Pulse: Failed to connect to server: %s\n",
716 pa_strerror(pa_context_errno(g_pContext))));
717 goto unlock_and_fail;
718 }
719
720 /* Wait until the g_pContext is ready */
721 for (;;)
722 {
723 pa_context_state_t cstate;
724 pa_threaded_mainloop_wait(g_pMainLoop);
725 cstate = pa_context_get_state(g_pContext);
726 if (cstate == PA_CONTEXT_READY)
727 break;
728 else if (cstate == PA_CONTEXT_TERMINATED || cstate == PA_CONTEXT_FAILED)
729 {
730 LogRel(("Pulse: Failed to initialize context (state %d)\n", cstate));
731 goto unlock_and_fail;
732 }
733 }
734 pa_threaded_mainloop_unlock(g_pMainLoop);
735
736 return &conf;
737
738unlock_and_fail:
739 if (g_pMainLoop)
740 pa_threaded_mainloop_unlock(g_pMainLoop);
741
742fail:
743 if (g_pMainLoop)
744 pa_threaded_mainloop_stop(g_pMainLoop);
745
746 if (g_pContext)
747 {
748 pa_context_disconnect(g_pContext);
749 pa_context_unref(g_pContext);
750 g_pContext = NULL;
751 }
752
753 if (g_pMainLoop)
754 {
755 pa_threaded_mainloop_free(g_pMainLoop);
756 g_pMainLoop = NULL;
757 }
758
759 return NULL;
760}
761
762static void pulse_audio_fini (void *opaque)
763{
764 if (g_pMainLoop)
765 pa_threaded_mainloop_stop(g_pMainLoop);
766
767 if (g_pContext)
768 {
769 pa_context_disconnect(g_pContext);
770 pa_context_unref(g_pContext);
771 g_pContext = NULL;
772 }
773
774 if (g_pMainLoop)
775 {
776 pa_threaded_mainloop_free(g_pMainLoop);
777 g_pMainLoop = NULL;
778 }
779
780 (void) opaque;
781}
782
783static struct audio_option pulse_options[] =
784{
785 {"DAC_MS", AUD_OPT_INT, &conf.buffer_msecs_out,
786 "DAC period size in milliseconds", NULL, 0},
787 {"ADC_MS", AUD_OPT_INT, &conf.buffer_msecs_in,
788 "ADC period size in milliseconds", NULL, 0},
789 {NULL, 0, NULL, NULL, NULL, 0}
790};
791
792static struct audio_pcm_ops pulse_pcm_ops =
793{
794 pulse_init_out,
795 pulse_fini_out,
796 pulse_run_out,
797 pulse_write,
798 pulse_ctl_out,
799
800 pulse_init_in,
801 pulse_fini_in,
802 pulse_run_in,
803 pulse_read,
804 pulse_ctl_in
805};
806
807struct audio_driver pulse_audio_driver =
808{
809 INIT_FIELD (name = ) "pulse",
810 INIT_FIELD (descr = ) "PulseAudio http://www.pulseaudio.org",
811 INIT_FIELD (options = ) pulse_options,
812 INIT_FIELD (init = ) pulse_audio_init,
813 INIT_FIELD (fini = ) pulse_audio_fini,
814 INIT_FIELD (pcm_ops = ) &pulse_pcm_ops,
815 INIT_FIELD (can_be_default = ) 1,
816 INIT_FIELD (max_voices_out = ) INT_MAX,
817 INIT_FIELD (max_voices_in = ) INT_MAX,
818 INIT_FIELD (voice_size_out = ) sizeof (PulseVoice),
819 INIT_FIELD (voice_size_in = ) sizeof (PulseVoice)
820};
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