VirtualBox

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

Last change on this file since 60404 was 59989, checked in by vboxsync, 8 years ago

Build fix, warnings.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.7 KB
Line 
1/* $Id: DrvHostPulseAudio.cpp 59989 2016-03-11 12:29:36Z 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 AUD_FMT_U8:
185 return PA_SAMPLE_U8;
186
187 case AUD_FMT_S16:
188 return PA_SAMPLE_S16LE;
189
190#ifdef PA_SAMPLE_S32LE
191 case AUD_FMT_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 = AUD_FMT_U8;
209 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
210 break;
211
212 case PA_SAMPLE_S16LE:
213 *pFmt = AUD_FMT_S16;
214 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
215 break;
216
217 case PA_SAMPLE_S16BE:
218 *pFmt = AUD_FMT_S16;
219 *pEndianness = PDMAUDIOENDIANNESS_BIG;
220 break;
221
222#ifdef PA_SAMPLE_S32LE
223 case PA_SAMPLE_S32LE:
224 *pFmt = AUD_FMT_S32;
225 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
226 break;
227#endif
228
229#ifdef PA_SAMPLE_S32BE
230 case PA_SAMPLE_S32BE:
231 *pFmt = AUD_FMT_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 pStrm->BufAttr.minreq = -1; /* Pulse should set something sensible for minreq on it's own */
639
640 /* Note that the struct BufAttr is updated to the obtained values after this call! */
641 char achName[64];
642 RTStrPrintf(achName, sizeof(achName), "%.32s (out)", pThis->pszStreamName);
643
644 int rc = paStreamOpen(pThis, false /* fIn */, achName, &pStrm->SampleSpec, &pStrm->BufAttr, &pStrm->pStream);
645 if (RT_FAILURE(rc))
646 return rc;
647
648 PDMAUDIOSTREAMCFG streamCfg;
649 rc = paPulseToFmt(pStrm->SampleSpec.format,
650 &streamCfg.enmFormat, &streamCfg.enmEndianness);
651 if (RT_FAILURE(rc))
652 {
653 LogRel(("PulseAudio: Cannot find audio output format %ld\n", pStrm->SampleSpec.format));
654 return rc;
655 }
656
657 streamCfg.uHz = pStrm->SampleSpec.rate;
658 streamCfg.cChannels = pStrm->SampleSpec.channels;
659
660 rc = DrvAudioStreamCfgToProps(&streamCfg, &pHstStrmOut->Props);
661 if (RT_SUCCESS(rc))
662 {
663 uint32_t cbBuf = RT_MIN(pStrm->BufAttr.tlength * 2,
664 pStrm->BufAttr.maxlength); /** @todo Make this configurable! */
665 if (cbBuf)
666 {
667 pStrm->pvPCMBuf = RTMemAllocZ(cbBuf);
668 if (pStrm->pvPCMBuf)
669 {
670 pStrm->cbPCMBuf = cbBuf;
671
672 uint32_t cSamples = cbBuf >> pHstStrmOut->Props.cShift;
673 if (pcSamples)
674 *pcSamples = cSamples;
675
676 /* Save pointer to driver instance. */
677 pStrm->pDrv = pThis;
678
679 LogFunc(("cbBuf=%RU32, cSamples=%RU32\n", cbBuf, cSamples));
680 }
681 else
682 rc = VERR_NO_MEMORY;
683 }
684 else
685 rc = VERR_INVALID_PARAMETER;
686 }
687
688 LogFlowFuncLeaveRC(rc);
689 return rc;
690}
691
692static DECLCALLBACK(bool) drvHostPulseAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
693{
694 NOREF(pInterface);
695 NOREF(enmDir);
696 return true; /* Always all enabled. */
697}
698
699static DECLCALLBACK(int) drvHostPulseAudioInitIn(PPDMIHOSTAUDIO pInterface,
700 PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
701 PDMAUDIORECSOURCE enmRecSource,
702 uint32_t *pcSamples)
703{
704 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
705 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
706 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
707 /* pcSamples is optional. */
708
709 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
710 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmIn;
711
712 LogFunc(("enmRecSrc=%ld\n", enmRecSource));
713
714 pStrm->SampleSpec.format = paFmtToPulse(pCfg->enmFormat);
715 pStrm->SampleSpec.rate = pCfg->uHz;
716 pStrm->SampleSpec.channels = pCfg->cChannels;
717
718 /* XXX check these values */
719 pStrm->BufAttr.fragsize = (pa_bytes_per_second(&pStrm->SampleSpec)
720 * s_pulseCfg.buffer_msecs_in) / 1000;
721 pStrm->BufAttr.maxlength = (pStrm->BufAttr.fragsize * 3) / 2;
722 /* Note: Other members of pa_buffer_attr are ignored for record streams. */
723
724 char achName[64];
725 RTStrPrintf(achName, sizeof(achName), "%.32s (in)", pThis->pszStreamName);
726
727 int rc = paStreamOpen(pThis, true /* fIn */, achName, &pStrm->SampleSpec, &pStrm->BufAttr,
728 &pStrm->pStream);
729 if (RT_FAILURE(rc))
730 return rc;
731
732 PDMAUDIOSTREAMCFG streamCfg;
733 rc = paPulseToFmt(pStrm->SampleSpec.format, &streamCfg.enmFormat,
734 &streamCfg.enmEndianness);
735 if (RT_FAILURE(rc))
736 {
737 LogRel(("PulseAudio: Cannot find audio capture format %ld\n", pStrm->SampleSpec.format));
738 return rc;
739 }
740
741 streamCfg.uHz = pStrm->SampleSpec.rate;
742 streamCfg.cChannels = pStrm->SampleSpec.channels;
743
744 rc = DrvAudioStreamCfgToProps(&streamCfg, &pHstStrmIn->Props);
745 if (RT_SUCCESS(rc))
746 {
747 uint32_t cSamples = RT_MIN(pStrm->BufAttr.fragsize * 10, pStrm->BufAttr.maxlength)
748 >> pHstStrmIn->Props.cShift;
749 LogFunc(("cShift=%RU8, cSamples=%RU32\n", pHstStrmIn->Props.cShift, cSamples));
750
751 if (pcSamples)
752 *pcSamples = cSamples;
753
754 /* Save pointer to driver instance. */
755 pStrm->pDrv = pThis;
756
757 pStrm->pu8PeekBuf = NULL;
758 }
759
760 LogFlowFuncLeaveRC(rc);
761 return rc;
762}
763
764static DECLCALLBACK(int) drvHostPulseAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
765 uint32_t *pcSamplesCaptured)
766{
767 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
768 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
769 /* pcSamplesPlayed is optional. */
770
771 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
772 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmIn;
773
774 /* We should only call pa_stream_readable_size() once and trust the first value. */
775 pa_threaded_mainloop_lock(pThis->pMainLoop);
776 size_t cbAvail = pa_stream_readable_size(pStrm->pStream);
777 pa_threaded_mainloop_unlock(pThis->pMainLoop);
778
779 if (cbAvail == (size_t)-1)
780 return paError(pStrm->pDrv, "Failed to determine input data size");
781
782 /* If the buffer was not dropped last call, add what remains. */
783 if (pStrm->pu8PeekBuf)
784 {
785 Assert(pStrm->cbPeekBuf >= pStrm->offPeekBuf);
786 cbAvail += (pStrm->cbPeekBuf - pStrm->offPeekBuf);
787 }
788
789 if (!cbAvail) /* No data? Bail out. */
790 {
791 if (pcSamplesCaptured)
792 *pcSamplesCaptured = 0;
793 return VINF_SUCCESS;
794 }
795
796 int rc = VINF_SUCCESS;
797
798 size_t cbToRead = RT_MIN(cbAvail, AudioMixBufFreeBytes(&pHstStrmIn->MixBuf));
799
800 LogFlowFunc(("cbToRead=%zu, cbAvail=%zu, offPeekBuf=%zu, cbPeekBuf=%zu\n",
801 cbToRead, cbAvail, pStrm->offPeekBuf, pStrm->cbPeekBuf));
802
803 size_t offWrite = 0;
804 uint32_t cWrittenTotal = 0;
805
806 while (cbToRead)
807 {
808 /* If there is no data, do another peek. */
809 if (!pStrm->pu8PeekBuf)
810 {
811 pa_threaded_mainloop_lock(pThis->pMainLoop);
812 pa_stream_peek(pStrm->pStream,
813 (const void**)&pStrm->pu8PeekBuf, &pStrm->cbPeekBuf);
814 pa_threaded_mainloop_unlock(pThis->pMainLoop);
815
816 pStrm->offPeekBuf = 0;
817
818 /* No data anymore?
819 * Note: If there's a data hole (cbPeekBuf then contains the length of the hole)
820 * we need to drop the stream lateron. */
821 if ( !pStrm->pu8PeekBuf
822 && !pStrm->cbPeekBuf)
823 {
824 break;
825 }
826 }
827
828 Assert(pStrm->cbPeekBuf >= pStrm->offPeekBuf);
829 size_t cbToWrite = RT_MIN(pStrm->cbPeekBuf - pStrm->offPeekBuf, cbToRead);
830
831 LogFlowFunc(("cbToRead=%zu, cbToWrite=%zu, offPeekBuf=%zu, cbPeekBuf=%zu, pu8PeekBuf=%p\n",
832 cbToRead, cbToWrite,
833 pStrm->offPeekBuf, pStrm->cbPeekBuf, pStrm->pu8PeekBuf));
834
835 if (cbToWrite)
836 {
837 uint32_t cWritten;
838 rc = AudioMixBufWriteCirc(&pHstStrmIn->MixBuf,
839 pStrm->pu8PeekBuf + pStrm->offPeekBuf,
840 cbToWrite, &cWritten);
841 if (RT_FAILURE(rc))
842 break;
843
844 uint32_t cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);
845
846 Assert(cbToRead >= cbWritten);
847 cbToRead -= cbWritten;
848 cWrittenTotal += cWritten;
849 pStrm->offPeekBuf += cbWritten;
850 }
851
852 if (/* Nothing to write anymore? Drop the buffer. */
853 !cbToWrite
854 /* Was there a hole in the peeking buffer? Drop it. */
855 || !pStrm->pu8PeekBuf
856 /* If the buffer is done, drop it. */
857 || pStrm->offPeekBuf == pStrm->cbPeekBuf)
858 {
859 pa_threaded_mainloop_lock(pThis->pMainLoop);
860 pa_stream_drop(pStrm->pStream);
861 pa_threaded_mainloop_unlock(pThis->pMainLoop);
862
863 pStrm->pu8PeekBuf = NULL;
864 }
865 }
866
867 if (RT_SUCCESS(rc))
868 {
869 uint32_t cProcessed = 0;
870 if (cWrittenTotal)
871 rc = AudioMixBufMixToParent(&pHstStrmIn->MixBuf, cWrittenTotal,
872 &cProcessed);
873
874 if (pcSamplesCaptured)
875 *pcSamplesCaptured = cWrittenTotal;
876
877 LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
878 cWrittenTotal, cProcessed, rc));
879 }
880
881 LogFlowFuncLeaveRC(rc);
882 return rc;
883}
884
885static DECLCALLBACK(int) drvHostPulseAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
886 uint32_t *pcSamplesPlayed)
887{
888 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
889 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
890 /* pcSamplesPlayed is optional. */
891
892 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
893 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmOut;
894
895 int rc = VINF_SUCCESS;
896 uint32_t cbReadTotal = 0;
897
898 uint32_t cLive = AudioMixBufAvail(&pHstStrmOut->MixBuf);
899 if (!cLive)
900 {
901 LogFlowFunc(("%p: No live samples, skipping\n", pHstStrmOut));
902
903 if (pcSamplesPlayed)
904 *pcSamplesPlayed = 0;
905 return VINF_SUCCESS;
906 }
907
908 pa_threaded_mainloop_lock(pThis->pMainLoop);
909
910 do
911 {
912 size_t cbWriteable = pa_stream_writable_size(pStrm->pStream);
913 if (cbWriteable == (size_t)-1)
914 {
915 rc = paError(pStrm->pDrv, "Failed to determine output data size");
916 break;
917 }
918
919 size_t cbLive = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cLive);
920 size_t cbToRead = RT_MIN(cbWriteable, cbLive);
921
922 LogFlowFunc(("cbToRead=%zu, cbWriteable=%zu, cbLive=%zu\n",
923 cbToRead, cbWriteable, cbLive));
924
925 uint32_t cRead, cbRead;
926 while (cbToRead)
927 {
928 rc = AudioMixBufReadCirc(&pHstStrmOut->MixBuf, pStrm->pvPCMBuf,
929 RT_MIN(cbToRead, pStrm->cbPCMBuf), &cRead);
930 if ( !cRead
931 || RT_FAILURE(rc))
932 {
933 break;
934 }
935
936 cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
937 if (pa_stream_write(pStrm->pStream, pStrm->pvPCMBuf, cbRead, NULL /* Cleanup callback */,
938 0, PA_SEEK_RELATIVE) < 0)
939 {
940 rc = paError(pStrm->pDrv, "Failed to write to output stream");
941 break;
942 }
943
944 Assert(cbToRead >= cbRead);
945 cbToRead -= cbRead;
946 cbReadTotal += cbRead;
947
948 LogFlowFunc(("\tcRead=%RU32 (%zu bytes) cbReadTotal=%RU32, cbToRead=%RU32\n",
949 cRead, AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead), cbReadTotal, cbToRead));
950 }
951
952 } while (0);
953
954 pa_threaded_mainloop_unlock(pThis->pMainLoop);
955
956 if (RT_SUCCESS(rc))
957 {
958 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbReadTotal);
959 if (cReadTotal)
960 AudioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
961
962 if (pcSamplesPlayed)
963 *pcSamplesPlayed = cReadTotal;
964
965 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n", cReadTotal, cbReadTotal, rc));
966 }
967
968 LogFlowFuncLeaveRC(rc);
969 return rc;
970}
971
972/** @todo Implement va handling. */
973static int paError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg)
974{
975 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
976 AssertPtrReturn(szMsg, VERR_INVALID_POINTER);
977
978 if (pThis->cLogErrors++ < VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS)
979 {
980 int rc2 = pa_context_errno(pThis->pContext);
981 LogRel(("PulseAudio: %s: %s\n", szMsg, pa_strerror(rc2)));
982 }
983
984 /** @todo Implement some PulseAudio -> IPRT mapping here. */
985 return VERR_GENERAL_FAILURE;
986}
987
988static void paEnumSinkCb(pa_context *pCtx, const pa_sink_info *pInfo, int eol, void *pvUserData)
989{
990 if (eol != 0)
991 return;
992
993 AssertPtrReturnVoid(pCtx);
994 AssertPtrReturnVoid(pInfo);
995
996 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
997 AssertPtrReturnVoid(pCbCtx);
998 AssertPtrReturnVoid(pCbCtx->pDrv);
999
1000 LogRel2(("PulseAudio: Using output sink '%s'\n", pInfo->name));
1001
1002 /** @todo Store sinks + channel mapping in callback context as soon as we have surround support. */
1003 pCbCtx->cDevOut++;
1004
1005 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
1006}
1007
1008static void paEnumSourceCb(pa_context *pCtx, const pa_source_info *pInfo, int eol, void *pvUserData)
1009{
1010 if (eol != 0)
1011 return;
1012
1013 AssertPtrReturnVoid(pCtx);
1014 AssertPtrReturnVoid(pInfo);
1015
1016 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
1017 AssertPtrReturnVoid(pCbCtx);
1018 AssertPtrReturnVoid(pCbCtx->pDrv);
1019
1020 LogRel2(("PulseAudio: Using input source '%s'\n", pInfo->name));
1021
1022 /** @todo Store sources + channel mapping in callback context as soon as we have surround support. */
1023 pCbCtx->cDevIn++;
1024
1025 pa_threaded_mainloop_signal(pCbCtx->pDrv->pMainLoop, 0);
1026}
1027
1028static void paEnumServerCb(pa_context *pCtx, const pa_server_info *pInfo, void *pvUserData)
1029{
1030 AssertPtrReturnVoid(pCtx);
1031 AssertPtrReturnVoid(pInfo);
1032
1033 PPULSEAUDIOENUMCBCTX pCbCtx = (PPULSEAUDIOENUMCBCTX)pvUserData;
1034 AssertPtrReturnVoid(pCbCtx);
1035
1036 PDRVHOSTPULSEAUDIO pThis = pCbCtx->pDrv;
1037 AssertPtrReturnVoid(pThis);
1038
1039 if (pInfo->default_sink_name)
1040 {
1041 Assert(RTStrIsValidEncoding(pInfo->default_sink_name));
1042 pCbCtx->pszDefaultSink = RTStrDup(pInfo->default_sink_name);
1043 }
1044
1045 if (pInfo->default_sink_name)
1046 {
1047 Assert(RTStrIsValidEncoding(pInfo->default_source_name));
1048 pCbCtx->pszDefaultSource = RTStrDup(pInfo->default_source_name);
1049 }
1050
1051 pa_threaded_mainloop_signal(pThis->pMainLoop, 0);
1052}
1053
1054static int paEnumerate(PDRVHOSTPULSEAUDIO pThis, PPDMAUDIOBACKENDCFG pCfg, uint32_t fEnum)
1055{
1056 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1057 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1058
1059 PDMAUDIOBACKENDCFG Cfg;
1060 RT_ZERO(Cfg);
1061
1062 Cfg.cbStreamOut = sizeof(PULSEAUDIOSTREAM);
1063 Cfg.cbStreamIn = sizeof(PULSEAUDIOSTREAM);
1064 Cfg.cMaxStreamsOut = UINT32_MAX;
1065 Cfg.cMaxStreamsIn = UINT32_MAX;
1066
1067 PULSEAUDIOENUMCBCTX cbCtx;
1068 RT_ZERO(cbCtx);
1069
1070 cbCtx.pDrv = pThis;
1071 cbCtx.fFlags = fEnum;
1072
1073 bool fLog = (fEnum & PULSEAUDIOENUMCBFLAGS_LOG);
1074
1075 int rc = paWaitFor(pThis, pa_context_get_server_info(pThis->pContext, paEnumServerCb, &cbCtx));
1076 if (RT_SUCCESS(rc))
1077 {
1078 if (cbCtx.pszDefaultSink)
1079 {
1080 if (fLog)
1081 LogRel2(("PulseAudio: Default output sink is '%s'\n", cbCtx.pszDefaultSink));
1082
1083 rc = paWaitFor(pThis, pa_context_get_sink_info_by_name(pThis->pContext, cbCtx.pszDefaultSink,
1084 paEnumSinkCb, &cbCtx));
1085 if ( RT_FAILURE(rc)
1086 && fLog)
1087 {
1088 LogRel(("PulseAudio: Error enumerating properties for default output sink '%s'\n", cbCtx.pszDefaultSink));
1089 }
1090 }
1091 else if (fLog)
1092 LogRel2(("PulseAudio: No default output sink found\n"));
1093
1094 if (RT_SUCCESS(rc))
1095 {
1096 if (cbCtx.pszDefaultSource)
1097 {
1098 if (fLog)
1099 LogRel2(("PulseAudio: Default input source is '%s'\n", cbCtx.pszDefaultSource));
1100
1101 rc = paWaitFor(pThis, pa_context_get_source_info_by_name(pThis->pContext, cbCtx.pszDefaultSource,
1102 paEnumSourceCb, &cbCtx));
1103 if ( RT_FAILURE(rc)
1104 && fLog)
1105 {
1106 LogRel(("PulseAudio: Error enumerating properties for default input source '%s'\n", cbCtx.pszDefaultSource));
1107 }
1108 }
1109 else if (fLog)
1110 LogRel2(("PulseAudio: No default input source found\n"));
1111 }
1112
1113 if (RT_SUCCESS(rc))
1114 {
1115 Cfg.cSinks = cbCtx.cDevOut;
1116 Cfg.cSources = cbCtx.cDevIn;
1117
1118 if (fLog)
1119 {
1120 LogRel2(("PulseAudio: Found %RU8 host playback device(s)\n", cbCtx.cDevOut));
1121 LogRel2(("PulseAudio: Found %RU8 host capturing device(s)\n", cbCtx.cDevIn));
1122 }
1123
1124 if (pCfg)
1125 memcpy(pCfg, &Cfg, sizeof(PDMAUDIOBACKENDCFG));
1126 }
1127
1128 if (cbCtx.pszDefaultSink)
1129 {
1130 RTStrFree(cbCtx.pszDefaultSink);
1131 cbCtx.pszDefaultSink = NULL;
1132 }
1133
1134 if (cbCtx.pszDefaultSource)
1135 {
1136 RTStrFree(cbCtx.pszDefaultSource);
1137 cbCtx.pszDefaultSource = NULL;
1138 }
1139 }
1140 else if (fLog)
1141 LogRel(("PulseAudio: Error enumerating PulseAudio server properties\n"));
1142
1143 LogFlowFuncLeaveRC(rc);
1144 return rc;
1145}
1146
1147static DECLCALLBACK(int) drvHostPulseAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
1148{
1149 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1150 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1151
1152 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1153 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmIn;
1154
1155 LogFlowFuncEnter();
1156
1157 if (pStrm->pStream)
1158 {
1159 pa_threaded_mainloop_lock(pThis->pMainLoop);
1160 pa_stream_disconnect(pStrm->pStream);
1161 pa_stream_unref(pStrm->pStream);
1162 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1163
1164 pStrm->pStream = NULL;
1165 }
1166
1167 return VINF_SUCCESS;
1168}
1169
1170static DECLCALLBACK(int) drvHostPulseAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
1171{
1172 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1173 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1174
1175 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1176 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmOut;
1177
1178 LogFlowFuncEnter();
1179
1180 if (pStrm->pStream)
1181 {
1182 pa_threaded_mainloop_lock(pThis->pMainLoop);
1183 pa_stream_disconnect(pStrm->pStream);
1184 pa_stream_unref(pStrm->pStream);
1185 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1186
1187 pStrm->pStream = NULL;
1188 }
1189
1190 if (pStrm->pvPCMBuf)
1191 {
1192 RTMemFree(pStrm->pvPCMBuf);
1193 pStrm->pvPCMBuf = NULL;
1194 pStrm->cbPCMBuf = 0;
1195 }
1196
1197 return VINF_SUCCESS;
1198}
1199
1200static DECLCALLBACK(int) drvHostPulseAudioControlOut(PPDMIHOSTAUDIO pInterface,
1201 PPDMAUDIOHSTSTRMOUT pHstStrmOut, PDMAUDIOSTREAMCMD enmStreamCmd)
1202{
1203 AssertPtrReturn(pInterface , VERR_INVALID_POINTER);
1204 AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
1205
1206 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1207 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmOut;
1208
1209 int rc = VINF_SUCCESS;
1210
1211 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1212
1213 switch (enmStreamCmd)
1214 {
1215 case PDMAUDIOSTREAMCMD_ENABLE:
1216 case PDMAUDIOSTREAMCMD_RESUME:
1217 {
1218 pa_threaded_mainloop_lock(pThis->pMainLoop);
1219
1220 if ( pStrm->pDrainOp
1221 && pa_operation_get_state(pStrm->pDrainOp) != PA_OPERATION_DONE)
1222 {
1223 pa_operation_cancel(pStrm->pDrainOp);
1224 pa_operation_unref(pStrm->pDrainOp);
1225
1226 pStrm->pDrainOp = NULL;
1227 }
1228 else
1229 {
1230 rc = paWaitFor(pThis, pa_stream_cork(pStrm->pStream, 0, paStreamCbSuccess, pStrm));
1231 }
1232
1233 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1234 break;
1235 }
1236
1237 case PDMAUDIOSTREAMCMD_DISABLE:
1238 case PDMAUDIOSTREAMCMD_PAUSE:
1239 {
1240 /* Pause audio output (the Pause bit of the AC97 x_CR register is set).
1241 * Note that we must return immediately from here! */
1242 pa_threaded_mainloop_lock(pThis->pMainLoop);
1243 if (!pStrm->pDrainOp)
1244 {
1245 rc = paWaitFor(pThis, pa_stream_trigger(pStrm->pStream, paStreamCbSuccess, pStrm));
1246 if (RT_LIKELY(RT_SUCCESS(rc)))
1247 pStrm->pDrainOp = pa_stream_drain(pStrm->pStream, paStreamCbDrain, pStrm);
1248 }
1249 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1250 break;
1251 }
1252
1253 default:
1254 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1255 rc = VERR_INVALID_PARAMETER;
1256 break;
1257 }
1258
1259 LogFlowFuncLeaveRC(rc);
1260 return rc;
1261}
1262
1263static DECLCALLBACK(int) drvHostPulseAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
1264 PDMAUDIOSTREAMCMD enmStreamCmd)
1265{
1266 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1267 AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
1268
1269 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1270 PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pHstStrmIn;
1271
1272 int rc = VINF_SUCCESS;
1273
1274 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1275
1276 switch (enmStreamCmd)
1277 {
1278 case PDMAUDIOSTREAMCMD_ENABLE:
1279 case PDMAUDIOSTREAMCMD_RESUME:
1280 {
1281 pa_threaded_mainloop_lock(pThis->pMainLoop);
1282 rc = paWaitFor(pThis, pa_stream_cork(pStrm->pStream, 0 /* Play / resume */, paStreamCbSuccess, pStrm));
1283 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1284 break;
1285 }
1286
1287 case PDMAUDIOSTREAMCMD_DISABLE:
1288 case PDMAUDIOSTREAMCMD_PAUSE:
1289 {
1290 pa_threaded_mainloop_lock(pThis->pMainLoop);
1291 if (pStrm->pu8PeekBuf) /* Do we need to drop the peek buffer?*/
1292 {
1293 pa_stream_drop(pStrm->pStream);
1294 pStrm->pu8PeekBuf = NULL;
1295 }
1296
1297 rc = paWaitFor(pThis, pa_stream_cork(pStrm->pStream, 1 /* Stop / pause */, paStreamCbSuccess, pStrm));
1298 pa_threaded_mainloop_unlock(pThis->pMainLoop);
1299 break;
1300 }
1301
1302 default:
1303 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1304 rc = VERR_INVALID_PARAMETER;
1305 break;
1306 }
1307
1308 return rc;
1309}
1310
1311static DECLCALLBACK(int) drvHostPulseAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
1312{
1313 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1314 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
1315
1316 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1317
1318 return paEnumerate(pThis, pCfg, PULSEAUDIOENUMCBFLAGS_LOG /* fEnum */);
1319}
1320
1321static DECLCALLBACK(void) drvHostPulseAudioShutdown(PPDMIHOSTAUDIO pInterface)
1322{
1323 AssertPtrReturnVoid(pInterface);
1324
1325 PDRVHOSTPULSEAUDIO pThis = PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface);
1326
1327 LogFlowFuncEnter();
1328
1329 if (pThis->pMainLoop)
1330 pa_threaded_mainloop_stop(pThis->pMainLoop);
1331
1332 if (pThis->pContext)
1333 {
1334 pa_context_disconnect(pThis->pContext);
1335 pa_context_unref(pThis->pContext);
1336 pThis->pContext = NULL;
1337 }
1338
1339 if (pThis->pMainLoop)
1340 {
1341 pa_threaded_mainloop_free(pThis->pMainLoop);
1342 pThis->pMainLoop = NULL;
1343 }
1344
1345 LogFlowFuncLeave();
1346}
1347
1348/**
1349 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1350 */
1351static DECLCALLBACK(void *) drvHostPulseAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1352{
1353 AssertPtrReturn(pInterface, NULL);
1354 AssertPtrReturn(pszIID, NULL);
1355
1356 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1357 PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
1358 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1359 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1360
1361 return NULL;
1362}
1363
1364/**
1365 * Constructs a PulseAudio Audio driver instance.
1366 *
1367 * @copydoc FNPDMDRVCONSTRUCT
1368 */
1369static DECLCALLBACK(int) drvHostPulseAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1370{
1371 AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
1372
1373 PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
1374 LogRel(("Audio: Initializing PulseAudio driver\n"));
1375
1376 CFGMR3QueryStringAlloc(pCfg, "StreamName", &pThis->pszStreamName);
1377
1378 pThis->pDrvIns = pDrvIns;
1379 /* IBase */
1380 pDrvIns->IBase.pfnQueryInterface = drvHostPulseAudioQueryInterface;
1381 /* IHostAudio */
1382 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostPulseAudio);
1383
1384 return VINF_SUCCESS;
1385}
1386
1387/**
1388 * Destructs a PulseAudio Audio driver instance.
1389 *
1390 * @copydoc FNPDMDRVCONSTRUCT
1391 */
1392static DECLCALLBACK(void) drvHostPulseAudioDestruct(PPDMDRVINS pDrvIns)
1393{
1394 PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
1395 LogFlowFuncEnter();
1396 if (pThis->pszStreamName)
1397 {
1398 MMR3HeapFree(pThis->pszStreamName);
1399 pThis->pszStreamName = NULL;
1400 }
1401}
1402
1403/**
1404 * Char driver registration record.
1405 */
1406const PDMDRVREG g_DrvHostPulseAudio =
1407{
1408 /* u32Version */
1409 PDM_DRVREG_VERSION,
1410 /* szName */
1411 "PulseAudio",
1412 /* szRCMod */
1413 "",
1414 /* szR0Mod */
1415 "",
1416 /* pszDescription */
1417 "Pulse Audio host driver",
1418 /* fFlags */
1419 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1420 /* fClass. */
1421 PDM_DRVREG_CLASS_AUDIO,
1422 /* cMaxInstances */
1423 ~0U,
1424 /* cbInstance */
1425 sizeof(DRVHOSTPULSEAUDIO),
1426 /* pfnConstruct */
1427 drvHostPulseAudioConstruct,
1428 /* pfnDestruct */
1429 drvHostPulseAudioDestruct,
1430 /* pfnRelocate */
1431 NULL,
1432 /* pfnIOCtl */
1433 NULL,
1434 /* pfnPowerOn */
1435 NULL,
1436 /* pfnReset */
1437 NULL,
1438 /* pfnSuspend */
1439 NULL,
1440 /* pfnResume */
1441 NULL,
1442 /* pfnAttach */
1443 NULL,
1444 /* pfnDetach */
1445 NULL,
1446 /* pfnPowerOff */
1447 NULL,
1448 /* pfnSoftReset */
1449 NULL,
1450 /* u32EndVersion */
1451 PDM_DRVREG_VERSION
1452};
1453
1454static struct audio_option pulse_options[] =
1455{
1456 {"DAC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_out,
1457 "DAC period size in milliseconds", NULL, 0},
1458 {"ADC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_in,
1459 "ADC period size in milliseconds", NULL, 0}
1460};
1461
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use