VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostPulseAudio.cpp@ 61050

Last change on this file since 61050 was 61050, checked in by vboxsync, 9 years ago

Audio: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.8 KB
Line 
1/* $Id: DrvHostPulseAudio.cpp 61050 2016-05-19 15:26:50Z vboxsync $ */
2/** @file
3 * VBox audio devices: Pulse Audio audio driver.
4 */
5
6/*
7 * Copyright (C) 2006-2016 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* Header Files *
20*********************************************************************************************************************************/
21#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
22#include <VBox/log.h>
23
24#include <stdio.h>
25
26#include <iprt/alloc.h>
27#include <iprt/mem.h>
28#include <iprt/uuid.h>
29
30RT_C_DECLS_BEGIN
31 #include "pulse_mangling.h"
32 #include "pulse_stubs.h"
33RT_C_DECLS_END
34
35#include <pulse/pulseaudio.h>
36
37#include "DrvAudio.h"
38#include "AudioMixBuffer.h"
39
40#include "VBoxDD.h"
41
42/*********************************************************************************************************************************
43* Defines *
44*********************************************************************************************************************************/
45#define VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS 32 /** @todo Make this configurable thru driver options. */
46
47#ifndef PA_STREAM_NOFLAGS
48# define PA_STREAM_NOFLAGS (pa_context_flags_t)0x0000U /* since 0.9.19 */
49#endif
50
51#ifndef PA_CONTEXT_NOFLAGS
52# define PA_CONTEXT_NOFLAGS (pa_context_flags_t)0x0000U /* since 0.9.19 */
53#endif
54
55/** No flags specified. */
56#define PULSEAUDIOENUMCBFLAGS_NONE 0
57/** (Release) log found devices. */
58#define PULSEAUDIOENUMCBFLAGS_LOG RT_BIT(0)
59
60/** Makes DRVHOSTPULSEAUDIO out of PDMIHOSTAUDIO. */
61#define PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface) \
62 ( (PDRVHOSTPULSEAUDIO)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTPULSEAUDIO, IHostAudio)) )
63
64/*********************************************************************************************************************************
65* Structures *
66*********************************************************************************************************************************/
67
68/**
69 * Host Pulse audio driver instance data.
70 * @implements PDMIAUDIOCONNECTOR
71 */
72typedef struct DRVHOSTPULSEAUDIO
73{
74 /** Pointer to the driver instance structure. */
75 PPDMDRVINS pDrvIns;
76 /** Pointer to PulseAudio's threaded main loop. */
77 pa_threaded_mainloop *pMainLoop;
78 /**
79 * Pointer to our PulseAudio context.
80 * Note: We use a pMainLoop in a separate thread (pContext).
81 * So either use callback functions or protect these functions
82 * by pa_threaded_mainloop_lock() / pa_threaded_mainloop_unlock().
83 */
84 pa_context *pContext;
85 /** Shutdown indicator. */
86 bool fLoopWait;
87 /** Pointer to host audio interface. */
88 PDMIHOSTAUDIO IHostAudio;
89 /** Error count for not flooding the release log.
90 * Specify UINT32_MAX for unlimited logging. */
91 uint32_t cLogErrors;
92 /** Configuration option: stream name. Optional. */
93 char *pszStreamName;
94} DRVHOSTPULSEAUDIO, *PDRVHOSTPULSEAUDIO;
95
96typedef struct PULSEAUDIOSTREAM
97{
98 /** Must come first, as this struct might be
99 * casted to one of these structs. */
100 union
101 {
102 PDMAUDIOHSTSTRMIN In;
103 PDMAUDIOHSTSTRMOUT Out;
104 };
105 /** Pointer to driver instance. */
106 PDRVHOSTPULSEAUDIO pDrv;
107 /** DAC/ADC buffer. */
108 void *pvPCMBuf;
109 /** Size (in bytes) of DAC/ADC buffer. */
110 uint32_t cbPCMBuf;
111 /** Pointer to opaque PulseAudio stream. */
112 pa_stream *pStream;
113 /** Pulse sample format and attribute specification. */
114 pa_sample_spec SampleSpec;
115 /** Pulse playback and buffer metrics. */
116 pa_buffer_attr BufAttr;
117 int fOpSuccess;
118 /** Pointer to Pulse sample peeking buffer. */
119 const uint8_t *pu8PeekBuf;
120 /** Current size (in bytes) of peeking data in
121 * buffer. */
122 size_t cbPeekBuf;
123 /** Our offset (in bytes) in peeking buffer. */
124 size_t offPeekBuf;
125 pa_operation *pDrainOp;
126} PULSEAUDIOSTREAM, *PPULSEAUDIOSTREAM;
127
128/* The desired buffer length in milliseconds. Will be the target total stream
129 * latency on newer version of pulse. Apparent latency can be less (or more.)
130 */
131typedef struct PULSEAUDIOCFG
132{
133 RTMSINTERVAL buffer_msecs_out;
134 RTMSINTERVAL buffer_msecs_in;
135} PULSEAUDIOCFG, *PPULSEAUDIOCFG;
136
137static PULSEAUDIOCFG s_pulseCfg =
138{
139 100, /* buffer_msecs_out */
140 100 /* buffer_msecs_in */
141};
142
143/**
144 * Callback context for server enumeration callbacks.
145 */
146typedef struct PULSEAUDIOENUMCBCTX
147{
148 /** Pointer to host backend driver. */
149 PDRVHOSTPULSEAUDIO pDrv;
150 /** Enumeration flags. */
151 uint32_t fFlags;
152 /** Number of found input devices. */
153 uint8_t cDevIn;
154 /** Number of found output devices. */
155 uint8_t cDevOut;
156 /** Name of default sink being used. Must be free'd using RTStrFree(). */
157 char *pszDefaultSink;
158 /** Name of default source being used. Must be free'd using RTStrFree(). */
159 char *pszDefaultSource;
160} PULSEAUDIOENUMCBCTX, *PPULSEAUDIOENUMCBCTX;
161
162/*********************************************************************************************************************************
163* Prototypes *
164*********************************************************************************************************************************/
165
166static int paEnumerate(PDRVHOSTPULSEAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum);
167static int paError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg);
168static void paStreamCbSuccess(pa_stream *pStream, int fSuccess, void *pvContext);
169
170/**
171 * Signal the main loop to abort. Just signalling isn't sufficient as the
172 * mainloop might not have been entered yet.
173 */
174static void paSignalWaiter(PDRVHOSTPULSEAUDIO pThis)
175{
176 pThis->fLoopWait = true;
177 pa_threaded_mainloop_signal(pThis->pMainLoop, 0);
178}
179
180static pa_sample_format_t paFmtToPulse(PDMAUDIOFMT fmt)
181{
182 switch (fmt)
183 {
184 case PDMAUDIOFMT_U8:
185 return PA_SAMPLE_U8;
186
187 case PDMAUDIOFMT_S16:
188 return PA_SAMPLE_S16LE;
189
190#ifdef PA_SAMPLE_S32LE
191 case PDMAUDIOFMT_S32:
192 return PA_SAMPLE_S32LE;
193#endif
194 default:
195 break;
196 }
197
198 AssertMsgFailed(("Format %ld not supported\n", fmt));
199 return PA_SAMPLE_U8;
200}
201
202static int paPulseToFmt(pa_sample_format_t pulsefmt,
203 PDMAUDIOFMT *pFmt, PDMAUDIOENDIANNESS *pEndianness)
204{
205 switch (pulsefmt)
206 {
207 case PA_SAMPLE_U8:
208 *pFmt = PDMAUDIOFMT_U8;
209 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
210 break;
211
212 case PA_SAMPLE_S16LE:
213 *pFmt = PDMAUDIOFMT_S16;
214 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
215 break;
216
217 case PA_SAMPLE_S16BE:
218 *pFmt = PDMAUDIOFMT_S16;
219 *pEndianness = PDMAUDIOENDIANNESS_BIG;
220 break;
221
222#ifdef PA_SAMPLE_S32LE
223 case PA_SAMPLE_S32LE:
224 *pFmt = PDMAUDIOFMT_S32;
225 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
226 break;
227#endif
228
229#ifdef PA_SAMPLE_S32BE
230 case PA_SAMPLE_S32BE:
231 *pFmt = PDMAUDIOFMT_S32;
232 *pEndianness = PDMAUDIOENDIANNESS_BIG;
233 break;
234#endif
235
236 default:
237 AssertMsgFailed(("Format %ld not supported\n", pulsefmt));
238 return VERR_NOT_SUPPORTED;
239 }
240
241 return VINF_SUCCESS;
242}
243
244/**
245 * Synchronously wait until an operation completed.
246 */
247static int paWaitForEx(PDRVHOSTPULSEAUDIO pThis, pa_operation *pOP, RTMSINTERVAL cMsTimeout)
248{
249 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
250 AssertPtrReturn(pOP, VERR_INVALID_POINTER);
251
252 int rc = VINF_SUCCESS;
253
254 uint64_t u64StartMs = RTTimeMilliTS();
255 while (pa_operation_get_state(pOP) == PA_OPERATION_RUNNING)
256 {
257 if (!pThis->fLoopWait)
258 {
259 AssertPtr(pThis->pMainLoop);
260 pa_threaded_mainloop_wait(pThis->pMainLoop);
261 }
262 pThis->fLoopWait = false;
263
264 uint64_t u64ElapsedMs = RTTimeMilliTS() - u64StartMs;
265 if (u64ElapsedMs >= cMsTimeout)
266 {
267 rc = VERR_TIMEOUT;
268 break;
269 }
270 }
271
272 pa_operation_unref(pOP);
273
274 return rc;
275}
276
277static int paWaitFor(PDRVHOSTPULSEAUDIO pThis, pa_operation *pOP)
278{
279 return paWaitForEx(pThis, pOP, 10 * 1000 /* 10s timeout */);
280}
281
282/**
283 * Context status changed.
284 */
285static void paContextCbStateChanged(pa_context *pCtx, void *pvUser)
286{
287 AssertPtrReturnVoid(pCtx);
288
289 PDRVHOSTPULSEAUDIO pThis = (PDRVHOSTPULSEAUDIO)pvUser;
290 AssertPtrReturnVoid(pThis);
291
292 switch (pa_context_get_state(pCtx))
293 {
294 case PA_CONTEXT_READY:
295 case PA_CONTEXT_TERMINATED:
296 paSignalWaiter(pThis);
297 break;
298
299 case PA_CONTEXT_FAILED:
300 LogRel(("PulseAudio: Audio context has failed, stopping\n"));
301 paSignalWaiter(pThis);
302 break;
303
304 default:
305 break;
306 }
307}
308
309/**
310 * Callback called when our pa_stream_drain operation was completed.
311 */
312static void paStreamCbDrain(pa_stream *pStream, int fSuccess, void *pvUser)
313{
314 AssertPtrReturnVoid(pStream);
315
316 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvUser;
317 AssertPtrReturnVoid(pStrm);
318
319 pStrm->fOpSuccess = fSuccess;
320 if (fSuccess)
321 {
322 pa_operation_unref(pa_stream_cork(pStream, 1,
323 paStreamCbSuccess, pvUser));
324 }
325 else
326 paError(pStrm->pDrv, "Failed to drain stream");
327
328 pa_operation_unref(pStrm->pDrainOp);
329 pStrm->pDrainOp = NULL;
330}
331
332/**
333 * Stream status changed.
334 */
335static void paStreamCbStateChanged(pa_stream *pStream, void *pvUser)
336{
337 AssertPtrReturnVoid(pStream);
338
339 PDRVHOSTPULSEAUDIO pThis = (PDRVHOSTPULSEAUDIO)pvUser;
340 AssertPtrReturnVoid(pThis);
341
342 switch (pa_stream_get_state(pStream))
343 {
344 case PA_STREAM_READY:
345 case PA_STREAM_FAILED:
346 case PA_STREAM_TERMINATED:
347 paSignalWaiter(pThis);
348 break;
349
350 default:
351 break;
352 }
353}
354
355static void paStreamCbSuccess(pa_stream *pStream, int fSuccess, void *pvUser)
356{
357 AssertPtrReturnVoid(pStream);
358
359 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvUser;
360 AssertPtrReturnVoid(pStrm);
361
362 pStrm->fOpSuccess = fSuccess;
363
364 if (fSuccess)
365 paSignalWaiter(pStrm->pDrv);
366 else
367 paError(pStrm->pDrv, "Failed to finish stream operation");
368}
369
370static int paStreamOpen(PDRVHOSTPULSEAUDIO pThis, bool fIn, const char *pszName,
371 pa_sample_spec *pSampleSpec, pa_buffer_attr *pBufAttr,
372 pa_stream **ppStream)
373{
374 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
375 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
376 AssertPtrReturn(pSampleSpec, VERR_INVALID_POINTER);
377 AssertPtrReturn(pBufAttr, VERR_INVALID_POINTER);
378 AssertPtrReturn(ppStream, VERR_INVALID_POINTER);
379
380 if (!pa_sample_spec_valid(pSampleSpec))
381 {
382 LogRel(("PulseAudio: Unsupported sample specification for stream \"%s\"\n",
383 pszName));
384 return VERR_NOT_SUPPORTED;
385 }
386
387 int rc = VINF_SUCCESS;
388
389 pa_stream *pStream = NULL;
390 uint32_t flags = PA_STREAM_NOFLAGS;
391
392 LogFunc(("Opening \"%s\", rate=%dHz, channels=%d, format=%s\n",
393 pszName, pSampleSpec->rate, pSampleSpec->channels,
394 pa_sample_format_to_string(pSampleSpec->format)));
395
396 pa_threaded_mainloop_lock(pThis->pMainLoop);
397
398 do
399 {
400 if (!(pStream = pa_stream_new(pThis->pContext, pszName, pSampleSpec,
401 NULL /* pa_channel_map */)))
402 {
403 LogRel(("PulseAudio: Could not create stream \"%s\"\n", pszName));
404 rc = VERR_NO_MEMORY;
405 break;
406 }
407
408 pa_stream_set_state_callback(pStream, paStreamCbStateChanged, pThis);
409
410#if PA_API_VERSION >= 12
411 /* XXX */
412 flags |= PA_STREAM_ADJUST_LATENCY;
413#endif
414
415#if 0
416 /* Not applicable as we don't use pa_stream_get_latency() and pa_stream_get_time(). */
417 flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
418#endif
419 /* No input/output right away after the stream was started. */
420 flags |= PA_STREAM_START_CORKED;
421
422 if (fIn)
423 {
424 LogFunc(("Input stream attributes: maxlength=%d fragsize=%d\n",
425 pBufAttr->maxlength, pBufAttr->fragsize));
426
427 if (pa_stream_connect_record(pStream, /*dev=*/NULL, pBufAttr, (pa_stream_flags_t)flags) < 0)
428 {
429 LogRel(("PulseAudio: Could not connect input stream \"%s\": %s\n",
430 pszName, pa_strerror(pa_context_errno(pThis->pContext))));
431 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
432 break;
433 }
434 }
435 else
436 {
437 LogFunc(("Output buffer attributes: maxlength=%d tlength=%d prebuf=%d minreq=%d\n",
438 pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
439
440 if (pa_stream_connect_playback(pStream, /*dev=*/NULL, pBufAttr, (pa_stream_flags_t)flags,
441 /*cvolume=*/NULL, /*sync_stream=*/NULL) < 0)
442 {
443 LogRel(("PulseAudio: Could not connect playback stream \"%s\": %s\n",
444 pszName, pa_strerror(pa_context_errno(pThis->pContext))));
445 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
446 break;
447 }
448 }
449
450 /* Wait until the stream is ready. */
451 for (;;)
452 {
453 if (!pThis->fLoopWait)
454 pa_threaded_mainloop_wait(pThis->pMainLoop);
455 pThis->fLoopWait = false;
456
457 pa_stream_state_t streamSt = pa_stream_get_state(pStream);
458 if (streamSt == PA_STREAM_READY)
459 break;
460 else if ( streamSt == PA_STREAM_FAILED
461 || streamSt == PA_STREAM_TERMINATED)
462 {
463 LogRel(("PulseAudio: Failed to initialize stream \"%s\" (state %ld)\n", pszName, streamSt));
464 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
465 break;
466 }
467 }
468
469 if (RT_FAILURE(rc))
470 break;
471
472 const pa_buffer_attr *pBufAttrObtained = pa_stream_get_buffer_attr(pStream);
473 AssertPtr(pBufAttrObtained);
474 memcpy(pBufAttr, pBufAttrObtained, sizeof(pa_buffer_attr));
475
476 if (fIn)
477 LogFunc(("Obtained record buffer attributes: maxlength=%RU32, fragsize=%RU32\n",
478 pBufAttr->maxlength, pBufAttr->fragsize));
479 else
480 LogFunc(("Obtained playback buffer attributes: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d\n",
481 pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
482
483 }
484 while (0);
485
486 if ( RT_FAILURE(rc)
487 && pStream)
488 pa_stream_disconnect(pStream);
489
490 pa_threaded_mainloop_unlock(pThis->pMainLoop);
491
492 if (RT_FAILURE(rc))
493 {
494 if (pStream)
495 pa_stream_unref(pStream);
496 }
497 else
498 *ppStream = pStream;
499
500 LogFlowFuncLeaveRC(rc);
501 return rc;
502}
503
504static DECLCALLBACK(int) drvHostPulseAudioInit(PPDMIHOSTAUDIO pInterface)
505{
506 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
507
508 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
509
510 LogFlowFuncEnter();
511
512 int rc = audioLoadPulseLib();
513 if (RT_FAILURE(rc))
514 {
515 LogRel(("PulseAudio: Failed to load the PulseAudio shared library! Error %Rrc\n", rc));
516 return rc;
517 }
518
519 pThis->fLoopWait = false;
520 pThis->pMainLoop = NULL;
521
522 bool fLocked = false;
523
524 do
525 {
526 if (!(pThis->pMainLoop = pa_threaded_mainloop_new()))
527 {
528 LogRel(("PulseAudio: Failed to allocate main loop: %s\n",
529 pa_strerror(pa_context_errno(pThis->pContext))));
530 rc = VERR_NO_MEMORY;
531 break;
532 }
533
534 if (!(pThis->pContext = pa_context_new(pa_threaded_mainloop_get_api(pThis->pMainLoop), "VirtualBox")))
535 {
536 LogRel(("PulseAudio: Failed to allocate context: %s\n",
537 pa_strerror(pa_context_errno(pThis->pContext))));
538 rc = VERR_NO_MEMORY;
539 break;
540 }
541
542 if (pa_threaded_mainloop_start(pThis->pMainLoop) < 0)
543 {
544 LogRel(("PulseAudio: Failed to start threaded mainloop: %s\n",
545 pa_strerror(pa_context_errno(pThis->pContext))));
546 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
547 break;
548 }
549
550 /* Install a global callback to known if something happens to our acquired context. */
551 pa_context_set_state_callback(pThis->pContext, paContextCbStateChanged, pThis /* pvUserData */);
552
553 pa_threaded_mainloop_lock(pThis->pMainLoop);
554 fLocked = true;
555
556 if (pa_context_connect(pThis->pContext, NULL /* pszServer */,
557 PA_CONTEXT_NOFLAGS, NULL) < 0)
558 {
559 LogRel(("PulseAudio: Failed to connect to server: %s\n",
560 pa_strerror(pa_context_errno(pThis->pContext))));
561 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
562 break;
563 }
564
565 /* Wait until the pThis->pContext is ready. */
566 for (;;)
567 {
568 if (!pThis->fLoopWait)
569 pa_threaded_mainloop_wait(pThis->pMainLoop);
570 pThis->fLoopWait = false;
571
572 pa_context_state_t cstate = pa_context_get_state(pThis->pContext);
573 if (cstate == PA_CONTEXT_READY)
574 break;
575 else if ( cstate == PA_CONTEXT_TERMINATED
576 || cstate == PA_CONTEXT_FAILED)
577 {
578 LogRel(("PulseAudio: Failed to initialize context (state %d)\n", cstate));
579 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
580 break;
581 }
582 }
583 }
584 while (0);
585
586 if (fLocked)
587 pa_threaded_mainloop_unlock(pThis->pMainLoop);
588
589 if (RT_FAILURE(rc))
590 {
591 if (pThis->pMainLoop)
592 pa_threaded_mainloop_stop(pThis->pMainLoop);
593
594 if (pThis->pContext)
595 {
596 pa_context_disconnect(pThis->pContext);
597 pa_context_unref(pThis->pContext);
598 pThis->pContext = NULL;
599 }
600
601 if (pThis->pMainLoop)
602 {
603 pa_threaded_mainloop_free(pThis->pMainLoop);
604 pThis->pMainLoop = NULL;
605 }
606 }
607
608 LogFlowFuncLeaveRC(rc);
609 return rc;
610}
611
612static DECLCALLBACK(int) drvHostPulseAudioInitOut(PPDMIHOSTAUDIO pInterface,
613 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
614 uint32_t *pcSamples)
615{
616 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
617 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
618 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
619 /* pcSamples is optional. */
620
621 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
622 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmOut;
623
624 LogFlowFuncEnter();
625
626 pStrm->pDrainOp = NULL;
627
628 pStrm->SampleSpec.format = paFmtToPulse(pCfg->enmFormat);
629 pStrm->SampleSpec.rate = pCfg->uHz;
630 pStrm->SampleSpec.channels = pCfg->cChannels;
631
632 /* Note that setting maxlength to -1 does not work on PulseAudio servers
633 * older than 0.9.10. So use the suggested value of 3/2 of tlength */
634 pStrm->BufAttr.tlength = (pa_bytes_per_second(&pStrm->SampleSpec)
635 * s_pulseCfg.buffer_msecs_out) / 1000;
636 pStrm->BufAttr.maxlength = (pStrm->BufAttr.tlength * 3) / 2;
637 pStrm->BufAttr.prebuf = -1; /* Same as tlength */
638
639 /* Set minreq to 0, as we want to control ourselves when to start/stop the stream. */
640 pStrm->BufAttr.minreq = 0;
641
642 /* Note that the struct BufAttr is updated to the obtained values after this call! */
643 char achName[64];
644 RTStrPrintf(achName, sizeof(achName), "%.32s (out)", pThis->pszStreamName);
645
646 int rc = paStreamOpen(pThis, false /* fIn */, achName, &pStrm->SampleSpec, &pStrm->BufAttr, &pStrm->pStream);
647 if (RT_FAILURE(rc))
648 return rc;
649
650 PDMAUDIOSTREAMCFG streamCfg;
651 rc = paPulseToFmt(pStrm->SampleSpec.format,
652 &streamCfg.enmFormat, &streamCfg.enmEndianness);
653 if (RT_FAILURE(rc))
654 {
655 LogRel(("PulseAudio: Cannot find audio output format %ld\n", pStrm->SampleSpec.format));
656 return rc;
657 }
658
659 streamCfg.uHz = pStrm->SampleSpec.rate;
660 streamCfg.cChannels = pStrm->SampleSpec.channels;
661
662 rc = DrvAudioStreamCfgToProps(&streamCfg, &pHstStrmOut->Props);
663 if (RT_SUCCESS(rc))
664 {
665 uint32_t cbBuf = RT_MIN(pStrm->BufAttr.tlength * 2,
666 pStrm->BufAttr.maxlength); /** @todo Make this configurable! */
667 if (cbBuf)
668 {
669 pStrm->pvPCMBuf = RTMemAllocZ(cbBuf);
670 if (pStrm->pvPCMBuf)
671 {
672 pStrm->cbPCMBuf = cbBuf;
673
674 uint32_t cSamples = cbBuf >> pHstStrmOut->Props.cShift;
675 if (pcSamples)
676 *pcSamples = cSamples;
677
678 /* Save pointer to driver instance. */
679 pStrm->pDrv = pThis;
680
681 LogFunc(("cbBuf=%RU32, cSamples=%RU32\n", cbBuf, cSamples));
682 }
683 else
684 rc = VERR_NO_MEMORY;
685 }
686 else
687 rc = VERR_INVALID_PARAMETER;
688 }
689
690 LogFlowFuncLeaveRC(rc);
691 return rc;
692}
693
694static DECLCALLBACK(bool) drvHostPulseAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
695{
696 NOREF(pInterface);
697 NOREF(enmDir);
698 return true; /* Always all enabled. */
699}
700
701static DECLCALLBACK(int) drvHostPulseAudioInitIn(PPDMIHOSTAUDIO pInterface,
702 PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
703 PDMAUDIORECSOURCE enmRecSource,
704 uint32_t *pcSamples)
705{
706 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
707 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
708 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
709 /* pcSamples is optional. */
710
711 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
712 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmIn;
713
714 LogFunc(("enmRecSrc=%ld\n", enmRecSource));
715
716 pStrm->SampleSpec.format = paFmtToPulse(pCfg->enmFormat);
717 pStrm->SampleSpec.rate = pCfg->uHz;
718 pStrm->SampleSpec.channels = pCfg->cChannels;
719
720 /* XXX check these values */
721 pStrm->BufAttr.fragsize = (pa_bytes_per_second(&pStrm->SampleSpec)
722 * s_pulseCfg.buffer_msecs_in) / 1000;
723 pStrm->BufAttr.maxlength = (pStrm->BufAttr.fragsize * 3) / 2;
724 /* Note: Other members of pa_buffer_attr are ignored for record streams. */
725
726 char achName[64];
727 RTStrPrintf(achName, sizeof(achName), "%.32s (in)", pThis->pszStreamName);
728
729 int rc = paStreamOpen(pThis, true /* fIn */, achName, &pStrm->SampleSpec, &pStrm->BufAttr,
730 &pStrm->pStream);
731 if (RT_FAILURE(rc))
732 return rc;
733
734 PDMAUDIOSTREAMCFG streamCfg;
735 rc = paPulseToFmt(pStrm->SampleSpec.format, &streamCfg.enmFormat,
736 &streamCfg.enmEndianness);
737 if (RT_FAILURE(rc))
738 {
739 LogRel(("PulseAudio: Cannot find audio capture format %ld\n", pStrm->SampleSpec.format));
740 return rc;
741 }
742
743 streamCfg.uHz = pStrm->SampleSpec.rate;
744 streamCfg.cChannels = pStrm->SampleSpec.channels;
745
746 rc = DrvAudioStreamCfgToProps(&streamCfg, &pHstStrmIn->Props);
747 if (RT_SUCCESS(rc))
748 {
749 uint32_t cSamples = RT_MIN(pStrm->BufAttr.fragsize * 10, pStrm->BufAttr.maxlength)
750 >> pHstStrmIn->Props.cShift;
751 LogFunc(("cShift=%RU8, cSamples=%RU32\n", pHstStrmIn->Props.cShift, cSamples));
752
753 if (pcSamples)
754 *pcSamples = cSamples;
755
756 /* Save pointer to driver instance. */
757 pStrm->pDrv = pThis;
758
759 pStrm->pu8PeekBuf = NULL;
760 }
761
762 LogFlowFuncLeaveRC(rc);
763 return rc;
764}
765
766static DECLCALLBACK(int) drvHostPulseAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
767 uint32_t *pcSamplesCaptured)
768{
769 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
770 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
771 /* pcSamplesPlayed is optional. */
772
773 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
774 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmIn;
775
776 /* We should only call pa_stream_readable_size() once and trust the first value. */
777 pa_threaded_mainloop_lock(pThis->pMainLoop);
778 size_t cbAvail = pa_stream_readable_size(pStrm->pStream);
779 pa_threaded_mainloop_unlock(pThis->pMainLoop);
780
781 if (cbAvail == (size_t)-1)
782 return paError(pStrm->pDrv, "Failed to determine input data size");
783
784 /* If the buffer was not dropped last call, add what remains. */
785 if (pStrm->pu8PeekBuf)
786 {
787 Assert(pStrm->cbPeekBuf >= pStrm->offPeekBuf);
788 cbAvail += (pStrm->cbPeekBuf - pStrm->offPeekBuf);
789 }
790
791 if (!cbAvail) /* No data? Bail out. */
792 {
793 if (pcSamplesCaptured)
794 *pcSamplesCaptured = 0;
795 return VINF_SUCCESS;
796 }
797
798 int rc = VINF_SUCCESS;
799
800 size_t cbToRead = RT_MIN(cbAvail, AudioMixBufFreeBytes(&pHstStrmIn->MixBuf));
801
802 LogFlowFunc(("cbToRead=%zu, cbAvail=%zu, offPeekBuf=%zu, cbPeekBuf=%zu\n",
803 cbToRead, cbAvail, pStrm->offPeekBuf, pStrm->cbPeekBuf));
804
805 size_t offWrite = 0;
806 uint32_t cWrittenTotal = 0;
807
808 while (cbToRead)
809 {
810 /* If there is no data, do another peek. */
811 if (!pStrm->pu8PeekBuf)
812 {
813 pa_threaded_mainloop_lock(pThis->pMainLoop);
814 pa_stream_peek(pStrm->pStream,
815 (const void**)&pStrm->pu8PeekBuf, &pStrm->cbPeekBuf);
816 pa_threaded_mainloop_unlock(pThis->pMainLoop);
817
818 pStrm->offPeekBuf = 0;
819
820 /* No data anymore?
821 * Note: If there's a data hole (cbPeekBuf then contains the length of the hole)
822 * we need to drop the stream lateron. */
823 if ( !pStrm->pu8PeekBuf
824 && !pStrm->cbPeekBuf)
825 {
826 break;
827 }
828 }
829
830 Assert(pStrm->cbPeekBuf >= pStrm->offPeekBuf);
831 size_t cbToWrite = RT_MIN(pStrm->cbPeekBuf - pStrm->offPeekBuf, cbToRead);
832
833 LogFlowFunc(("cbToRead=%zu, cbToWrite=%zu, offPeekBuf=%zu, cbPeekBuf=%zu, pu8PeekBuf=%p\n",
834 cbToRead, cbToWrite,
835 pStrm->offPeekBuf, pStrm->cbPeekBuf, pStrm->pu8PeekBuf));
836
837 if (cbToWrite)
838 {
839 uint32_t cWritten;
840 rc = AudioMixBufWriteCirc(&pHstStrmIn->MixBuf,
841 pStrm->pu8PeekBuf + pStrm->offPeekBuf,
842 cbToWrite, &cWritten);
843 if (RT_FAILURE(rc))
844 break;
845
846 uint32_t cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);
847
848 Assert(cbToRead >= cbWritten);
849 cbToRead -= cbWritten;
850 cWrittenTotal += cWritten;
851 pStrm->offPeekBuf += cbWritten;
852 }
853
854 if (/* Nothing to write anymore? Drop the buffer. */
855 !cbToWrite
856 /* Was there a hole in the peeking buffer? Drop it. */
857 || !pStrm->pu8PeekBuf
858 /* If the buffer is done, drop it. */
859 || pStrm->offPeekBuf == pStrm->cbPeekBuf)
860 {
861 pa_threaded_mainloop_lock(pThis->pMainLoop);
862 pa_stream_drop(pStrm->pStream);
863 pa_threaded_mainloop_unlock(pThis->pMainLoop);
864
865 pStrm->pu8PeekBuf = NULL;
866 }
867 }
868
869 if (RT_SUCCESS(rc))
870 {
871 uint32_t cProcessed = 0;
872 if (cWrittenTotal)
873 rc = AudioMixBufMixToParent(&pHstStrmIn->MixBuf, cWrittenTotal,
874 &cProcessed);
875
876 if (pcSamplesCaptured)
877 *pcSamplesCaptured = cWrittenTotal;
878
879 LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
880 cWrittenTotal, cProcessed, rc));
881 }
882
883 LogFlowFuncLeaveRC(rc);
884 return rc;
885}
886
887static DECLCALLBACK(int) drvHostPulseAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
888 uint32_t *pcSamplesPlayed)
889{
890 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
891 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
892 /* pcSamplesPlayed is optional. */
893
894 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
895 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmOut;
896
897 int rc = VINF_SUCCESS;
898 uint32_t cbReadTotal = 0;
899
900 uint32_t cLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
901 if (!cLive)
902 {
903#if 0
904 LogFlowFunc(("No live samples, skipping\n"));
905#endif
906 if (pcSamplesPlayed)
907 *pcSamplesPlayed = 0;
908 return VINF_SUCCESS;
909 }
910
911 pa_threaded_mainloop_lock(pThis->pMainLoop);
912
913 do
914 {
915 size_t cbWriteable = pa_stream_writable_size(pStrm->pStream);
916 if (cbWriteable == (size_t)-1)
917 {
918 rc = paError(pStrm->pDrv, "Failed to determine output data size");
919 break;
920 }
921
922 size_t cbLive = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cLive);
923 size_t cbToRead = RT_MIN(cbWriteable, cbLive);
924
925 LogFlowFunc(("cbToRead=%zu, cbWriteable=%zu, cbLive=%zu\n",
926 cbToRead, cbWriteable, cbLive));
927
928 uint32_t cRead, cbRead;
929 while (cbToRead)
930 {
931 rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf, pStrm->pvPCMBuf,
932 RT_MIN(cbToRead, pStrm->cbPCMBuf), &cRead);
933 if ( !cRead
934 || RT_FAILURE(rc))
935 {
936 break;
937 }
938
939 cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
940 if (pa_stream_write(pStrm->pStream, pStrm->pvPCMBuf, cbRead, NULL /* Cleanup callback */,
941 0, PA_SEEK_RELATIVE) < 0)
942 {
943 rc = paError(pStrm->pDrv, "Failed to write to output stream");
944 break;
945 }
946
947 Assert(cbToRead >= cbRead);
948 cbToRead -= cbRead;
949 cbReadTotal += cbRead;
950
951 LogFlowFunc(("\tcRead=%RU32 (%zu bytes) cbReadTotal=%RU32, cbToRead=%RU32\n",
952 cRead, AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead), cbReadTotal, cbToRead));
953 }
954
955 } while (0);
956
957 pa_threaded_mainloop_unlock(pThis->pMainLoop);
958
959 if (RT_SUCCESS(rc))
960 {
961 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbReadTotal);
962 if (cReadTotal)
963 AudioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
964
965 if (pcSamplesPlayed)
966 *pcSamplesPlayed = cReadTotal;
967
968 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n", cReadTotal, cbReadTotal, rc));
969 }
970
971 LogFlowFuncLeaveRC(rc);
972 return rc;
973}
974
975/** @todo Implement va handling. */
976static int paError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg)
977{
978 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
979 AssertPtrReturn(szMsg, VERR_INVALID_POINTER);
980
981 if (pThis->cLogErrors++ < VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS)
982 {
983 int rc2 = pa_context_errno(pThis->pContext);
984 LogRel(("PulseAudio: %s: %s\n", szMsg, pa_strerror(rc2)));
985 }
986
987 /** @todo Implement some PulseAudio -> IPRT mapping here. */
988 return VERR_GENERAL_FAILURE;
989}
990
991static void paEnumSinkCb(pa_context *pCtx, const pa_sink_info *pInfo, int eol, void *pvUserData)
992{
993 if (eol != 0)
994 return;
995
996 AssertPtrReturnVoid(pCtx);
997 AssertPtrReturnVoid(pInfo);
998
999 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
1000 AssertPtrReturnVoid(pCbCtx);
1001 AssertPtrReturnVoid(pCbCtx->pDrv);
1002
1003 LogRel2(("PulseAudio: Using output sink '%s'\n", pInfo->name));
1004
1005 /** @todo Store sinks + channel mapping in callback context as soon as we have surround support. */
1006 pCbCtx->cDevOut++;
1007
1008 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
1009}
1010
1011static void paEnumSourceCb(pa_context *pCtx, const pa_source_info *pInfo, int eol, void *pvUserData)
1012{
1013 if (eol != 0)
1014 return;
1015
1016 AssertPtrReturnVoid(pCtx);
1017 AssertPtrReturnVoid(pInfo);
1018
1019 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
1020 AssertPtrReturnVoid(pCbCtx);
1021 AssertPtrReturnVoid(pCbCtx->pDrv);
1022
1023 LogRel2(("PulseAudio: Using input source '%s'\n", pInfo->name));
1024
1025 /** @todo Store sources + channel mapping in callback context as soon as we have surround support. */
1026 pCbCtx->cDevIn++;
1027
1028 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
1029}
1030
1031static void paEnumServerCb(pa_context *pCtx, const pa_server_info *pInfo, void *pvUserData)
1032{
1033 AssertPtrReturnVoid(pCtx);
1034 AssertPtrReturnVoid(pInfo);
1035
1036 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
1037 AssertPtrReturnVoid(pCbCtx);
1038
1039 PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv;
1040 AssertPtrReturnVoid(pThis);
1041
1042 if (pInfo->default_sink_name)
1043 {
1044 Assert(RTStrIsValidEncoding(pInfo->default_sink_name));
1045 pCbCtx->pszDefaultSink = RTStrDup(pInfo->default_sink_name);
1046 }
1047
1048 if (pInfo->default_sink_name)
1049 {
1050 Assert(RTStrIsValidEncoding(pInfo->default_source_name));
1051 pCbCtx->pszDefaultSource = RTStrDup(pInfo->default_source_name);
1052 }
1053
1054 pa_threaded_mainloop_signal(pThis->pMainLoop, 0);
1055}
1056
1057static int paEnumerate(PDRVHOSTPULSEAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum)
1058{
1059 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1060 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1061
1062 PDMAUDIOBACKENDCFG Cfg;
1063 RT_ZERO(Cfg);
1064
1065 Cfg.cbStreamOut = sizeof(PULSEAUDIOSTREAM);
1066 Cfg.cbStreamIn = sizeof(PULSEAUDIOSTREAM);
1067 Cfg.cMaxStreamsOut = UINT32_MAX;
1068 Cfg.cMaxStreamsIn = UINT32_MAX;
1069
1070 PULSEAUDIOENUMCBCTX cbCtx;
1071 RT_ZERO(cbCtx);
1072
1073 cbCtx.pDrv = pThis;
1074 cbCtx.fFlags = fEnum;
1075
1076 bool fLog = (fEnum & PULSEAUDIOENUMCBFLAGS_LOG);
1077
1078 int rc = paWaitFor(pThis, pa_context_get_server_info(pThis->pContext, paEnumServerCb, &cbCtx));
1079 if (RT_SUCCESS(rc))
1080 {
1081 if (cbCtx.pszDefaultSink)
1082 {
1083 if (fLog)
1084 LogRel2(("PulseAudio: Default output sink is '%s'\n", cbCtx.pszDefaultSink));
1085
1086 rc = paWaitFor(pThis, pa_context_get_sink_info_by_name(pThis->pContext, cbCtx.pszDefaultSink,
1087 paEnumSinkCb, &cbCtx));
1088 if ( RT_FAILURE(rc)
1089 && fLog)
1090 {
1091 LogRel(("PulseAudio: Error enumerating properties for default output sink '%s'\n", cbCtx.pszDefaultSink));
1092 }
1093 }
1094 else if (fLog)
1095 LogRel2(("PulseAudio: No default output sink found\n"));
1096
1097 if (RT_SUCCESS(rc))
1098 {
1099 if (cbCtx.pszDefaultSource)
1100 {
1101 if (fLog)
1102 LogRel2(("PulseAudio: Default input source is '%s'\n", cbCtx.pszDefaultSource));
1103
1104 rc = paWaitFor(pThis, pa_context_get_source_info_by_name(pThis->pContext, cbCtx.pszDefaultSource,
1105 paEnumSourceCb, &cbCtx));
1106 if ( RT_FAILURE(rc)
1107 && fLog)
1108 {
1109 LogRel(("PulseAudio: Error enumerating properties for default input source '%s'\n", cbCtx.pszDefaultSource));
1110 }
1111 }
1112 else if (fLog)
1113 LogRel2(("PulseAudio: No default input source found\n"));
1114 }
1115
1116 if (RT_SUCCESS(rc))
1117 {
1118 Cfg.cSinks = cbCtx.cDevOut;
1119 Cfg.cSources = cbCtx.cDevIn;
1120
1121 if (fLog)
1122 {
1123 LogRel2(("PulseAudio: Found %RU8 host playback device(s)\n", cbCtx.cDevOut));
1124 LogRel2(("PulseAudio: Found %RU8 host capturing device(s)\n", cbCtx.cDevIn));
1125 }
1126
1127 if (pCfg)
1128 memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG));
1129 }
1130
1131 if (cbCtx.pszDefaultSink)
1132 {
1133 RTStrFree(cbCtx.pszDefaultSink);
1134 cbCtx.pszDefaultSink = NULL;
1135 }
1136
1137 if (cbCtx.pszDefaultSource)
1138 {
1139 RTStrFree(cbCtx.pszDefaultSource);
1140 cbCtx.pszDefaultSource = NULL;
1141 }
1142 }
1143 else if (fLog)
1144 LogRel(("PulseAudio: Error enumerating PulseAudio server properties\n"));
1145
1146 LogFlowFuncLeaveRC(rc);
1147 return rc;
1148}
1149
1150static DECLCALLBACK(int) drvHostPulseAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
1151{
1152 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1153 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1154
1155 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1156 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmIn;
1157
1158 LogFlowFuncEnter();
1159
1160 if (pStrm->pStream)
1161 {
1162 pa_threaded_mainloop_lock(pThis->pMainLoop);
1163 pa_stream_disconnect(pStrm->pStream);
1164 pa_stream_unref(pStrm->pStream);
1165 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1166
1167 pStrm->pStream = NULL;
1168 }
1169
1170 return VINF_SUCCESS;
1171}
1172
1173static DECLCALLBACK(int) drvHostPulseAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
1174{
1175 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1176 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1177
1178 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1179 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmOut;
1180
1181 LogFlowFuncEnter();
1182
1183 if (pStrm->pStream)
1184 {
1185 pa_threaded_mainloop_lock(pThis->pMainLoop);
1186 pa_stream_disconnect(pStrm->pStream);
1187 pa_stream_unref(pStrm->pStream);
1188 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1189
1190 pStrm->pStream = NULL;
1191 }
1192
1193 if (pStrm->pvPCMBuf)
1194 {
1195 RTMemFree(pStrm->pvPCMBuf);
1196 pStrm->pvPCMBuf = NULL;
1197 pStrm->cbPCMBuf = 0;
1198 }
1199
1200 return VINF_SUCCESS;
1201}
1202
1203static DECLCALLBACK(int) drvHostPulseAudioControlOut(PPDMIHOSTAUDIO pInterface,
1204 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PDMAUDIOSTREAMCMD enmStreamCmd)
1205{
1206 AssertPtrReturn(pInterface , VERR_INVALID_POINTER);
1207 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1208
1209 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1210 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmOut;
1211
1212 int rc = VINF_SUCCESS;
1213
1214 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1215
1216 switch (enmStreamCmd)
1217 {
1218 case PDMAUDIOSTREAMCMD_ENABLE:
1219 case PDMAUDIOSTREAMCMD_RESUME:
1220 {
1221 pa_threaded_mainloop_lock(pThis->pMainLoop);
1222
1223 if ( pStrm->pDrainOp
1224 && pa_operation_get_state(pStrm->pDrainOp) != PA_OPERATION_DONE)
1225 {
1226 pa_operation_cancel(pStrm->pDrainOp);
1227 pa_operation_unref(pStrm->pDrainOp);
1228
1229 pStrm->pDrainOp = NULL;
1230 }
1231 else
1232 {
1233 rc = paWaitFor(pThis, pa_stream_cork(pStrm->pStream, 0, paStreamCbSuccess, pStrm));
1234 }
1235
1236 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1237 break;
1238 }
1239
1240 case PDMAUDIOSTREAMCMD_DISABLE:
1241 case PDMAUDIOSTREAMCMD_PAUSE:
1242 {
1243 /* Pause audio output (the Pause bit of the AC97 x_CR register is set).
1244 * Note that we must return immediately from here! */
1245 pa_threaded_mainloop_lock(pThis->pMainLoop);
1246 if (!pStrm->pDrainOp)
1247 {
1248 rc = paWaitFor(pThis, pa_stream_trigger(pStrm->pStream, paStreamCbSuccess, pStrm));
1249 if (RT_LIKELY(RT_SUCCESS(rc)))
1250 pStrm->pDrainOp = pa_stream_drain(pStrm->pStream, paStreamCbDrain, pStrm);
1251 }
1252 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1253 break;
1254 }
1255
1256 default:
1257 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1258 rc = VERR_INVALID_PARAMETER;
1259 break;
1260 }
1261
1262 LogFlowFuncLeaveRC(rc);
1263 return rc;
1264}
1265
1266static DECLCALLBACK(int) drvHostPulseAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
1267 PDMAUDIOSTREAMCMD enmStreamCmd)
1268{
1269 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1270 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1271
1272 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1273 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmIn;
1274
1275 int rc = VINF_SUCCESS;
1276
1277 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1278
1279 switch (enmStreamCmd)
1280 {
1281 case PDMAUDIOSTREAMCMD_ENABLE:
1282 case PDMAUDIOSTREAMCMD_RESUME:
1283 {
1284 pa_threaded_mainloop_lock(pThis->pMainLoop);
1285 rc = paWaitFor(pThis, pa_stream_cork(pStrm->pStream, 0 /* Play / resume */, paStreamCbSuccess, pStrm));
1286 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1287 break;
1288 }
1289
1290 case PDMAUDIOSTREAMCMD_DISABLE:
1291 case PDMAUDIOSTREAMCMD_PAUSE:
1292 {
1293 pa_threaded_mainloop_lock(pThis->pMainLoop);
1294 if (pStrm->pu8PeekBuf) /* Do we need to drop the peek buffer?*/
1295 {
1296 pa_stream_drop(pStrm->pStream);
1297 pStrm->pu8PeekBuf = NULL;
1298 }
1299
1300 rc = paWaitFor(pThis, pa_stream_cork(pStrm->pStream, 1 /* Stop / pause */, paStreamCbSuccess, pStrm));
1301 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1302 break;
1303 }
1304
1305 default:
1306 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1307 rc = VERR_INVALID_PARAMETER;
1308 break;
1309 }
1310
1311 return rc;
1312}
1313
1314static DECLCALLBACK(int) drvHostPulseAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
1315{
1316 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1317 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1318
1319 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1320
1321 return paEnumerate(pThis, pCfg, PULSEAUDIOENUMCBFLAGS_LOG /* fEnum */);
1322}
1323
1324static DECLCALLBACK(void) drvHostPulseAudioShutdown(PPDMIHOSTAUDIO pInterface)
1325{
1326 AssertPtrReturnVoid(pInterface);
1327
1328 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1329
1330 LogFlowFuncEnter();
1331
1332 if (pThis->pMainLoop)
1333 pa_threaded_mainloop_stop(pThis->pMainLoop);
1334
1335 if (pThis->pContext)
1336 {
1337 pa_context_disconnect(pThis->pContext);
1338 pa_context_unref(pThis->pContext);
1339 pThis->pContext = NULL;
1340 }
1341
1342 if (pThis->pMainLoop)
1343 {
1344 pa_threaded_mainloop_free(pThis->pMainLoop);
1345 pThis->pMainLoop = NULL;
1346 }
1347
1348 LogFlowFuncLeave();
1349}
1350
1351/**
1352 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1353 */
1354static DECLCALLBACK(void *) drvHostPulseAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1355{
1356 AssertPtrReturn(pInterface, NULL);
1357 AssertPtrReturn(pszIID, NULL);
1358
1359 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1360 PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
1361 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1362 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1363
1364 return NULL;
1365}
1366
1367/**
1368 * Constructs a PulseAudio Audio driver instance.
1369 *
1370 * @copydoc FNPDMDRVCONSTRUCT
1371 */
1372static DECLCALLBACK(int) drvHostPulseAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1373{
1374 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1375
1376 PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
1377 LogRel(("Audio: Initializing PulseAudio driver\n"));
1378
1379 CFGMR3QueryStringAlloc(pCfg, "StreamName", &pThis->pszStreamName);
1380
1381 pThis->pDrvIns = pDrvIns;
1382 /* IBase */
1383 pDrvIns->IBase.pfnQueryInterface = drvHostPulseAudioQueryInterface;
1384 /* IHostAudio */
1385 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostPulseAudio);
1386
1387 return VINF_SUCCESS;
1388}
1389
1390/**
1391 * Destructs a PulseAudio Audio driver instance.
1392 *
1393 * @copydoc FNPDMDRVCONSTRUCT
1394 */
1395static DECLCALLBACK(void) drvHostPulseAudioDestruct(PPDMDRVINS pDrvIns)
1396{
1397 PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
1398 LogFlowFuncEnter();
1399 if (pThis->pszStreamName)
1400 {
1401 MMR3HeapFree(pThis->pszStreamName);
1402 pThis->pszStreamName = NULL;
1403 }
1404}
1405
1406/**
1407 * Char driver registration record.
1408 */
1409const PDMDRVREG g_DrvHostPulseAudio =
1410{
1411 /* u32Version */
1412 PDM_DRVREG_VERSION,
1413 /* szName */
1414 "PulseAudio",
1415 /* szRCMod */
1416 "",
1417 /* szR0Mod */
1418 "",
1419 /* pszDescription */
1420 "Pulse Audio host driver",
1421 /* fFlags */
1422 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1423 /* fClass. */
1424 PDM_DRVREG_CLASS_AUDIO,
1425 /* cMaxInstances */
1426 ~0U,
1427 /* cbInstance */
1428 sizeof(DRVHOSTPULSEAUDIO),
1429 /* pfnConstruct */
1430 drvHostPulseAudioConstruct,
1431 /* pfnDestruct */
1432 drvHostPulseAudioDestruct,
1433 /* pfnRelocate */
1434 NULL,
1435 /* pfnIOCtl */
1436 NULL,
1437 /* pfnPowerOn */
1438 NULL,
1439 /* pfnReset */
1440 NULL,
1441 /* pfnSuspend */
1442 NULL,
1443 /* pfnResume */
1444 NULL,
1445 /* pfnAttach */
1446 NULL,
1447 /* pfnDetach */
1448 NULL,
1449 /* pfnPowerOff */
1450 NULL,
1451 /* pfnSoftReset */
1452 NULL,
1453 /* u32EndVersion */
1454 PDM_DRVREG_VERSION
1455};
1456
1457static struct audio_option pulse_options[] =
1458{
1459 {"DAC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_out,
1460 "DAC period size in milliseconds", NULL, 0},
1461 {"ADC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_in,
1462 "ADC period size in milliseconds", NULL, 0}
1463};
1464
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