VirtualBox

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

Last change on this file since 49482 was 45136, checked in by vboxsync, 12 years ago

Devices/Audio: mangle symbols of libasound and libpulse, otherwise they can be resolved by dynamic libraries if -fvisibility is not supported

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