VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio_old/DrvHostPulseAudio.cpp@ 61541

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

Audio: Use the old audio code for now when doing a release. Set VBOX_WITH_AUDIO_STABLE to an empty value to enable the new audio code (default for non-release builds for now).

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