7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/* $Id$ */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/** @file
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * VBox audio devices: Pulse Audio audio driver.
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/*
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * Copyright (C) 2006-2015 Oracle Corporation
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync *
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * available from http://www.virtualbox.org. This file is free software;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * you can redistribute it and/or modify it under the terms of the GNU
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * General Public License (GPL) as published by the Free Software
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/*******************************************************************************
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync* Header Files *
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync*******************************************************************************/
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#include <stdio.h>
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#include <iprt/alloc.h>
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#include <iprt/mem.h>
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#include <iprt/uuid.h>
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncRT_C_DECLS_BEGIN
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync #include "pulse_mangling.h"
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync #include "pulse_stubs.h"
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncRT_C_DECLS_END
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#include <pulse/pulseaudio.h>
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#include "vl_vbox.h"
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#include "DrvAudio.h"
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#include "AudioMixBuffer.h"
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#ifdef LOG_GROUP
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync# undef LOG_GROUP
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#endif
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#define LOG_GROUP LOG_GROUP_DEV_AUDIO
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#include <VBox/log.h>
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#define VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS 32 /** @todo Make this configurable thru driver options. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#ifndef PA_STREAM_NOFLAGS
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync# define PA_STREAM_NOFLAGS (pa_context_flags_t)0x0000U /* since 0.9.19 */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#endif
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#ifndef PA_CONTEXT_NOFLAGS
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync# define PA_CONTEXT_NOFLAGS (pa_context_flags_t)0x0000U /* since 0.9.19 */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#endif
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/*
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * We use a g_pMainLoop in a separate thread g_pContext. We have to call functions for
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * manipulating objects either from callback functions or we have to protect
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * these functions by pa_threaded_mainloop_lock() / pa_threaded_mainloop_unlock().
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic struct pa_threaded_mainloop *g_pMainLoop;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic struct pa_context *g_pContext;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/**
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * Host Pulse audio driver instance data.
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * @implements PDMIAUDIOCONNECTOR
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsynctypedef struct DRVHOSTPULSEAUDIO
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /** Pointer to the driver instance structure. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPDMDRVINS pDrvIns;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /** Pointer to host audio interface. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDMIHOSTAUDIO IHostAudio;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /** Error count for not flooding the release log.
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * UINT32_MAX for unlimited logging. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t cLogErrors;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync} DRVHOSTPULSEAUDIO, *PDRVHOSTPULSEAUDIO;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsynctypedef struct PULSEAUDIOSTREAM
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /** Must come first, as this struct might be
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * casted to one of these structs. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync union
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDMAUDIOHSTSTRMIN In;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDMAUDIOHSTSTRMOUT Out;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync } hw;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /** Pointer to driver instance. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDRVHOSTPULSEAUDIO pDrv;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /** DAC/ADC buffer. */
605036d491298181444650ae12453c9207a7cf01vboxsync void *pvPCMBuf;
605036d491298181444650ae12453c9207a7cf01vboxsync /** Size (in bytes) of DAC/ADC buffer. */
605036d491298181444650ae12453c9207a7cf01vboxsync uint32_t cbPCMBuf;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /** Pointer to opaque PulseAudio stream. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream *pStream;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /** Pulse sample format and attribute specification. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_sample_spec SampleSpec;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /** Pulse playback and buffer metrics. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_buffer_attr BufAttr;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync int fOpSuccess;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /** Pointer to Pulse sample peeking buffer. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync const uint8_t *pu8PeekBuf;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /** Current size (in bytes) of peeking data in
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * buffer. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync size_t cbPeekBuf;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /** Our offset (in bytes) in peeking buffer. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync size_t offPeekBuf;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_operation *pDrainOp;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync} PULSEAUDIOSTREAM, *PPULSEAUDIOSTREAM;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/* The desired buffer length in milliseconds. Will be the target total stream
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * latency on newer version of pulse. Apparent latency can be less (or more.)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsynctypedef struct PULSEAUDIOCFG
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync RTMSINTERVAL buffer_msecs_out;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync RTMSINTERVAL buffer_msecs_in;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync} PULSEAUDIOCFG, *PPULSEAUDIOCFG;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic PULSEAUDIOCFG s_pulseCfg =
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync 100, /* buffer_msecs_out */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync 100 /* buffer_msecs_in */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync};
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/** Makes DRVHOSTPULSEAUDIO out of PDMIHOSTAUDIO. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#define PDMIHOSTAUDIO_2_DRVHOSTPULSEAUDIO(pInterface) \
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync ( (PDRVHOSTPULSEAUDIO)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTPULSEAUDIO, IHostAudio)) )
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic int drvHostPulseAudioError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic void drvHostPulseAudioCbSuccess(pa_stream *pStream, int fSuccess, void *pvContext);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic pa_sample_format_t drvHostPulseAudioFmtToPulse(PDMAUDIOFMT fmt)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync switch (fmt)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case AUD_FMT_U8:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return PA_SAMPLE_U8;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case AUD_FMT_S16:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return PA_SAMPLE_S16LE;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#ifdef PA_SAMPLE_S32LE
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case AUD_FMT_S32:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return PA_SAMPLE_S32LE;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#endif
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync default:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertMsgFailed(("Format %ld not supported\n", fmt));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return PA_SAMPLE_U8;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic int drvHostPulseAudioPulseToFmt(pa_sample_format_t pulsefmt,
005de8319b7bbb65252f4ecad4b4b8f650908d4dvboxsync PDMAUDIOFMT *pFmt, PDMAUDIOENDIANNESS *pEndianness)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync switch (pulsefmt)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PA_SAMPLE_U8:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync *pFmt = AUD_FMT_U8;
005de8319b7bbb65252f4ecad4b4b8f650908d4dvboxsync *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PA_SAMPLE_S16LE:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync *pFmt = AUD_FMT_S16;
005de8319b7bbb65252f4ecad4b4b8f650908d4dvboxsync *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PA_SAMPLE_S16BE:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync *pFmt = AUD_FMT_S16;
005de8319b7bbb65252f4ecad4b4b8f650908d4dvboxsync *pEndianness = PDMAUDIOENDIANNESS_BIG;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#ifdef PA_SAMPLE_S32LE
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PA_SAMPLE_S32LE:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync *pFmt = AUD_FMT_S32;
005de8319b7bbb65252f4ecad4b4b8f650908d4dvboxsync *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#endif
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#ifdef PA_SAMPLE_S32BE
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PA_SAMPLE_S32BE:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync *pFmt = AUD_FMT_S32;
005de8319b7bbb65252f4ecad4b4b8f650908d4dvboxsync *pEndianness = PDMAUDIOENDIANNESS_BIG;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#endif
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync default:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertMsgFailed(("Format %ld not supported\n", pulsefmt));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return VERR_NOT_SUPPORTED;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return VINF_SUCCESS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/**
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * Synchronously wait until an operation completed.
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic int drvHostPulseAudioWaitFor(pa_operation *pOP, RTMSINTERVAL cMsTimeout)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pOP, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync int rc = VINF_SUCCESS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pOP)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint64_t u64StartMs = RTTimeMilliTS();
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint64_t u64ElapsedMs;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync while (pa_operation_get_state(pOP) == PA_OPERATION_RUNNING)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_wait(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync u64ElapsedMs = RTTimeMilliTS() - u64StartMs;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (u64ElapsedMs >= cMsTimeout)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = VERR_TIMEOUT;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_operation_unref(pOP);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/**
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * Context status changed.
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic void drvHostPulseAudioCbCtxState(pa_context *pContext, void *pvContext)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturnVoid(pContext);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(pStrm);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync switch (pa_context_get_state(pContext))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PA_CONTEXT_READY:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PA_CONTEXT_TERMINATED:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_signal(g_pMainLoop, 0);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PA_CONTEXT_FAILED:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Audio input/output stopped!\n"));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_signal(g_pMainLoop, 0);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync default:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/**
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * Callback called when our pa_stream_drain operation was completed.
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic void drvHostPulseAudioCbStreamDrain(pa_stream *pStream, int fSuccess, void *pvContext)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturnVoid(pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturnVoid(pStrm);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pStrm->fOpSuccess = fSuccess;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (fSuccess)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_operation_unref(pa_stream_cork(pStream, 1,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync drvHostPulseAudioCbSuccess, pvContext));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync else
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync drvHostPulseAudioError(pStrm->pDrv, "Failed to drain stream");
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_operation_unref(pStrm->pDrainOp);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pStrm->pDrainOp = NULL;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/**
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * Stream status changed.
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic void drvHostPulseAudioCbStreamState(pa_stream *pStream, void *pvContext)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturnVoid(pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(pvContext);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync switch (pa_stream_get_state(pStream))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PA_STREAM_READY:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PA_STREAM_FAILED:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PA_STREAM_TERMINATED:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_signal(g_pMainLoop, 0 /* fWait */);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync default:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic void drvHostPulseAudioCbSuccess(pa_stream *pStream, int fSuccess, void *pvContext)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturnVoid(pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPULSEAUDIOSTREAM pStrm = (PPULSEAUDIOSTREAM)pvContext;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturnVoid(pStrm);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pStrm->fOpSuccess = fSuccess;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (fSuccess)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_signal(g_pMainLoop, 0 /* fWait */);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync else
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync drvHostPulseAudioError(pStrm->pDrv, "Failed to finish stream operation");
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic int drvHostPulseAudioOpen(bool fIn, const char *pszName,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_sample_spec *pSampleSpec, pa_buffer_attr *pBufAttr,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream **ppStream)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pszName, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pSampleSpec, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pBufAttr, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(ppStream, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (!pa_sample_spec_valid(pSampleSpec))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Unsupported sample specification for stream \"%s\"\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pszName));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return VERR_NOT_SUPPORTED;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync int rc = VINF_SUCCESS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream *pStream = NULL;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t flags = PA_STREAM_NOFLAGS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFunc(("Opening \"%s\", rate=%dHz, channels=%d, format=%s\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pszName, pSampleSpec->rate, pSampleSpec->channels,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_sample_format_to_string(pSampleSpec->format)));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_lock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync do
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (!(pStream = pa_stream_new(g_pContext, pszName, pSampleSpec,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NULL /* pa_channel_map */)))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Could not create stream \"%s\"\n", pszName));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = VERR_NO_MEMORY;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream_set_state_callback(pStream, drvHostPulseAudioCbStreamState, NULL);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#if PA_API_VERSION >= 12
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* XXX */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync flags |= PA_STREAM_ADJUST_LATENCY;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#endif
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#if 0
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* Not applicable as we don't use pa_stream_get_latency() and pa_stream_get_time(). */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync#endif
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* No input/output right away after the stream was started. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync flags |= PA_STREAM_START_CORKED;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (fIn)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFunc(("Input stream attributes: maxlength=%d fragsize=%d\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pBufAttr->maxlength, pBufAttr->fragsize));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pa_stream_connect_record(pStream, /*dev=*/NULL, pBufAttr, (pa_stream_flags_t)flags) < 0)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Could not connect input stream \"%s\": %s\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pszName, pa_strerror(pa_context_errno(g_pContext))));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync else
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFunc(("Output buffer attributes: maxlength=%d tlength=%d prebuf=%d minreq=%d\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pa_stream_connect_playback(pStream, /*dev=*/NULL, pBufAttr, (pa_stream_flags_t)flags,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /*cvolume=*/NULL, /*sync_stream=*/NULL) < 0)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Could not connect playback stream \"%s\": %s\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pszName, pa_strerror(pa_context_errno(g_pContext))));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* Wait until the stream is ready. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream_state_t sstate;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync for (;;)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_wait(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync sstate = pa_stream_get_state(pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (sstate == PA_STREAM_READY)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync else if ( sstate == PA_STREAM_FAILED
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync || sstate == PA_STREAM_TERMINATED)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Failed to initialize stream \"%s\" (state %ld)\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pszName, sstate));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_FAILURE(rc))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync const pa_buffer_attr *pBufAttrObtained = pa_stream_get_buffer_attr(pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtr(pBufAttrObtained);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync memcpy(pBufAttr, pBufAttrObtained, sizeof(pa_buffer_attr));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (fIn)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFunc(("Obtained record buffer attributes: maxlength=%RU32, fragsize=%RU32\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pBufAttr->maxlength, pBufAttr->fragsize));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync else
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFunc(("Obtained playback buffer attributes: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync while (0);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_FAILURE(rc))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pStream)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream_disconnect(pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pStream)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream_unref(pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync else
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync *ppStream = pStream;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFuncLeaveRC(rc);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic DECLCALLBACK(int) drvHostPulseAudioInit(PPDMIHOSTAUDIO pInterface)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(pInterface);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFuncEnter();
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync int rc = audioLoadPulseLib();
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_FAILURE(rc))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Failed to load the PulseAudio shared library! Error %Rrc\n", rc));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync bool fLocked = false;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync do
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (!(g_pMainLoop = pa_threaded_mainloop_new()))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Failed to allocate main loop: %s\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_strerror(pa_context_errno(g_pContext))));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = VERR_NO_MEMORY;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (!(g_pContext = pa_context_new(pa_threaded_mainloop_get_api(g_pMainLoop), "VirtualBox")))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Failed to allocate context: %s\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_strerror(pa_context_errno(g_pContext))));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = VERR_NO_MEMORY;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pa_threaded_mainloop_start(g_pMainLoop) < 0)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Failed to start threaded mainloop: %s\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_strerror(pa_context_errno(g_pContext))));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_context_set_state_callback(g_pContext, drvHostPulseAudioCbCtxState, NULL);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_lock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync fLocked = true;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pa_context_connect(g_pContext, NULL /* pszServer */,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PA_CONTEXT_NOFLAGS, NULL) < 0)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Failed to connect to server: %s\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_strerror(pa_context_errno(g_pContext))));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* Wait until the g_pContext is ready */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync for (;;)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_context_state_t cstate;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_wait(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync cstate = pa_context_get_state(g_pContext);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (cstate == PA_CONTEXT_READY)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync else if ( cstate == PA_CONTEXT_TERMINATED
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync || cstate == PA_CONTEXT_FAILED)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Failed to initialize context (state %d)\n", cstate));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync while (0);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_FAILURE(rc))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (g_pMainLoop)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (fLocked)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (g_pMainLoop)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_stop(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (g_pContext)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_context_disconnect(g_pContext);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_context_unref(g_pContext);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync g_pContext = NULL;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (g_pMainLoop)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_free(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync g_pMainLoop = NULL;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFuncLeaveRC(rc);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic DECLCALLBACK(int) drvHostPulseAudioInitOut(PPDMIHOSTAUDIO pInterface,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPDMAUDIOHSTSTRMOUT pHstStrmOut, PPDMAUDIOSTREAMCFG pCfg,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t *pcSamples)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(pInterface);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pcSamples is optional. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPULSEAUDIOSTREAM pThisStrmOut = (PPULSEAUDIOSTREAM)pHstStrmOut;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFuncEnter();
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmOut->pDrainOp = NULL;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmOut->SampleSpec.format = drvHostPulseAudioFmtToPulse(pCfg->enmFormat);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmOut->SampleSpec.rate = pCfg->uHz;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmOut->SampleSpec.channels = pCfg->cChannels;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* Note that setting maxlength to -1 does not work on PulseAudio servers
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * older than 0.9.10. So use the suggested value of 3/2 of tlength */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmOut->BufAttr.tlength = (pa_bytes_per_second(&pThisStrmOut->SampleSpec)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * s_pulseCfg.buffer_msecs_out) / 1000;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmOut->BufAttr.maxlength = (pThisStrmOut->BufAttr.tlength * 3) / 2;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmOut->BufAttr.prebuf = -1; /* Same as tlength */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmOut->BufAttr.minreq = -1; /* Pulse should set something sensible for minreq on it's own */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* Note that the struct BufAttr is updated to the obtained values after this call! */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync int rc = drvHostPulseAudioOpen(false /* fIn */, "pa.out", &pThisStrmOut->SampleSpec, &pThisStrmOut->BufAttr,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync &pThisStrmOut->pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_FAILURE(rc))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDMAUDIOSTREAMCFG streamCfg;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = drvHostPulseAudioPulseToFmt(pThisStrmOut->SampleSpec.format,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync &streamCfg.enmFormat, &streamCfg.enmEndianness);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_FAILURE(rc))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Cannot find audio output format %ld\n", pThisStrmOut->SampleSpec.format));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync streamCfg.uHz = pThisStrmOut->SampleSpec.rate;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync streamCfg.cChannels = pThisStrmOut->SampleSpec.channels;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = drvAudioStreamCfgToProps(&streamCfg, &pHstStrmOut->Props);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_SUCCESS(rc))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
605036d491298181444650ae12453c9207a7cf01vboxsync uint32_t cbBuf = RT_MIN(pThisStrmOut->BufAttr.tlength * 2,
605036d491298181444650ae12453c9207a7cf01vboxsync pThisStrmOut->BufAttr.maxlength); /** @todo Make this configurable! */
005de8319b7bbb65252f4ecad4b4b8f650908d4dvboxsync if (cbBuf)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
605036d491298181444650ae12453c9207a7cf01vboxsync pThisStrmOut->pvPCMBuf = RTMemAllocZ(cbBuf);
605036d491298181444650ae12453c9207a7cf01vboxsync if (pThisStrmOut->pvPCMBuf)
605036d491298181444650ae12453c9207a7cf01vboxsync {
605036d491298181444650ae12453c9207a7cf01vboxsync pThisStrmOut->cbPCMBuf = cbBuf;
605036d491298181444650ae12453c9207a7cf01vboxsync
605036d491298181444650ae12453c9207a7cf01vboxsync uint32_t cSamples = cbBuf >> pHstStrmOut->Props.cShift;
605036d491298181444650ae12453c9207a7cf01vboxsync if (pcSamples)
605036d491298181444650ae12453c9207a7cf01vboxsync *pcSamples = cSamples;
605036d491298181444650ae12453c9207a7cf01vboxsync
605036d491298181444650ae12453c9207a7cf01vboxsync LogFunc(("cbBuf=%RU32, cSamples=%RU32\n", cbBuf, cSamples));
605036d491298181444650ae12453c9207a7cf01vboxsync }
605036d491298181444650ae12453c9207a7cf01vboxsync else
605036d491298181444650ae12453c9207a7cf01vboxsync rc = VERR_NO_MEMORY;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync else
605036d491298181444650ae12453c9207a7cf01vboxsync rc = VERR_INVALID_PARAMETER;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFuncLeaveRC(rc);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic DECLCALLBACK(bool) drvHostPulseAudioIsEnabled(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(pInterface);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(enmDir);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return true; /* Always all enabled. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic DECLCALLBACK(int) drvHostPulseAudioInitIn(PPDMIHOSTAUDIO pInterface,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPDMAUDIOHSTSTRMIN pHstStrmIn, PPDMAUDIOSTREAMCFG pCfg,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDMAUDIORECSOURCE enmRecSource,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t *pcSamples)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(pInterface);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pcSamples is optional. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPULSEAUDIOSTREAM pThisStrmIn = (PPULSEAUDIOSTREAM)pHstStrmIn;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFunc(("enmRecSrc=%ld\n", enmRecSource));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmIn->SampleSpec.format = drvHostPulseAudioFmtToPulse(pCfg->enmFormat);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmIn->SampleSpec.rate = pCfg->uHz;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmIn->SampleSpec.channels = pCfg->cChannels;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* XXX check these values */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmIn->BufAttr.fragsize = (pa_bytes_per_second(&pThisStrmIn->SampleSpec)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * s_pulseCfg.buffer_msecs_in) / 1000;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmIn->BufAttr.maxlength = (pThisStrmIn->BufAttr.fragsize * 3) / 2;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* Note: Other members of pa_buffer_attr are ignored for record streams. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync int rc = drvHostPulseAudioOpen(true /* fIn */, "pa.in", &pThisStrmIn->SampleSpec, &pThisStrmIn->BufAttr,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync &pThisStrmIn->pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_FAILURE(rc))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDMAUDIOSTREAMCFG streamCfg;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = drvHostPulseAudioPulseToFmt(pThisStrmIn->SampleSpec.format, &streamCfg.enmFormat,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync &streamCfg.enmEndianness);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_FAILURE(rc))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: Cannot find audio capture format %ld\n", pThisStrmIn->SampleSpec.format));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync streamCfg.uHz = pThisStrmIn->SampleSpec.rate;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync streamCfg.cChannels = pThisStrmIn->SampleSpec.channels;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = drvAudioStreamCfgToProps(&streamCfg, &pHstStrmIn->Props);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_SUCCESS(rc))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t cSamples = RT_MIN(pThisStrmIn->BufAttr.fragsize * 10, pThisStrmIn->BufAttr.maxlength)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync >> pHstStrmIn->Props.cShift;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFunc(("cShift=%RU8, cSamples=%RU32\n", pHstStrmIn->Props.cShift, cSamples));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pcSamples)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync *pcSamples = cSamples;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmIn->pu8PeekBuf = NULL;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFuncLeaveRC(rc);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic DECLCALLBACK(int) drvHostPulseAudioCaptureIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t *pcSamplesCaptured)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(pInterface);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pcSamplesPlayed is optional. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPULSEAUDIOSTREAM pThisStrmIn = (PPULSEAUDIOSTREAM)pHstStrmIn;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* We should only call pa_stream_readable_size() once and trust the first value. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_lock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync size_t cbAvail = pa_stream_readable_size(pThisStrmIn->pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (cbAvail == (size_t)-1)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return drvHostPulseAudioError(pThisStrmIn->pDrv, "Failed to determine input data size");
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* If the buffer was not dropped last call, add what remains. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pThisStrmIn->pu8PeekBuf)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync Assert(pThisStrmIn->cbPeekBuf >= pThisStrmIn->offPeekBuf);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync cbAvail += (pThisStrmIn->cbPeekBuf - pThisStrmIn->offPeekBuf);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (!cbAvail) /* No data? Bail out. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pcSamplesCaptured)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync *pcSamplesCaptured = 0;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return VINF_SUCCESS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync int rc = VINF_SUCCESS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync size_t cbToRead = RT_MIN(cbAvail, audioMixBufFreeBytes(&pHstStrmIn->MixBuf));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFunc(("cbToRead=%zu, cbAvail=%zu, offPeekBuf=%zu, cbPeekBuf=%zu\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync cbToRead, cbAvail, pThisStrmIn->offPeekBuf, pThisStrmIn->cbPeekBuf));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync size_t offWrite = 0;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t cWrittenTotal = 0;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync while (cbToRead)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* If there is no data, do another peek. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (!pThisStrmIn->pu8PeekBuf)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_lock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream_peek(pThisStrmIn->pStream,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync (const void**)&pThisStrmIn->pu8PeekBuf, &pThisStrmIn->cbPeekBuf);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmIn->offPeekBuf = 0;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* No data anymore?
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * Note: If there's a data hole (cbPeekBuf then contains the length of the hole)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * we need to drop the stream lateron. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if ( !pThisStrmIn->pu8PeekBuf
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync && !pThisStrmIn->cbPeekBuf)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync Assert(pThisStrmIn->cbPeekBuf >= pThisStrmIn->offPeekBuf);
64c999f63b03ee002fa6489fcb278b3a0ddd46dbvboxsync size_t cbToWrite = RT_MIN(pThisStrmIn->cbPeekBuf - pThisStrmIn->offPeekBuf, cbToRead);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFunc(("cbToRead=%zu, cbToWrite=%zu, offPeekBuf=%zu, cbPeekBuf=%zu, pu8PeekBuf=%p\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync cbToRead, cbToWrite,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmIn->offPeekBuf, pThisStrmIn->cbPeekBuf, pThisStrmIn->pu8PeekBuf));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (cbToWrite)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t cWritten;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = audioMixBufWriteCirc(&pHstStrmIn->MixBuf,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmIn->pu8PeekBuf + pThisStrmIn->offPeekBuf,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync cbToWrite, &cWritten);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_FAILURE(rc))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t cbWritten = AUDIOMIXBUF_S2B(&pHstStrmIn->MixBuf, cWritten);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync Assert(cbToRead >= cbWritten);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync cbToRead -= cbWritten;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync cWrittenTotal += cWritten;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmIn->offPeekBuf += cbWritten;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (/* Nothing to write anymore? Drop the buffer. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync !cbToWrite
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* Was there a hole in the peeking buffer? Drop it. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync || !pThisStrmIn->pu8PeekBuf
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* If the buffer is done, drop it. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync || pThisStrmIn->offPeekBuf == pThisStrmIn->cbPeekBuf)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_lock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream_drop(pThisStrmIn->pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmIn->pu8PeekBuf = NULL;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_SUCCESS(rc))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t cProcessed = 0;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (cWrittenTotal)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = audioMixBufMixToParent(&pHstStrmIn->MixBuf, cWrittenTotal,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync &cProcessed);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pcSamplesCaptured)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync *pcSamplesCaptured = cWrittenTotal;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync cWrittenTotal, cProcessed, rc));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFuncLeaveRC(rc);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic DECLCALLBACK(int) drvHostPulseAudioPlayOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t *pcSamplesPlayed)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(pInterface);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pcSamplesPlayed is optional. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPULSEAUDIOSTREAM pThisStrmOut = (PPULSEAUDIOSTREAM)pHstStrmOut;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync int rc = VINF_SUCCESS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t cbReadTotal = 0;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
605036d491298181444650ae12453c9207a7cf01vboxsync uint32_t cLive = drvAudioHstOutSamplesLive(pHstStrmOut, NULL /* pcStreamsLive */);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (!cLive)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFunc(("%p: No live samples, skipping\n", pHstStrmOut));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pcSamplesPlayed)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync *pcSamplesPlayed = 0;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return VINF_SUCCESS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_lock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync do
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync size_t cbWriteable = pa_stream_writable_size(pThisStrmOut->pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (cbWriteable == (size_t)-1)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = drvHostPulseAudioError(pThisStrmOut->pDrv, "Failed to determine output data size");
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync size_t cbLive = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cLive);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync size_t cbToRead = RT_MIN(cbWriteable, cbLive);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFunc(("cbToRead=%zu, cbWriteable=%zu, cbLive=%zu\n",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync cbToRead, cbWriteable, cbLive));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t cRead, cbRead;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync while (cbToRead)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
005de8319b7bbb65252f4ecad4b4b8f650908d4dvboxsync rc = audioMixBufReadCirc(&pHstStrmOut->MixBuf, pThisStrmOut->pvPCMBuf,
605036d491298181444650ae12453c9207a7cf01vboxsync RT_MIN(cbToRead, pThisStrmOut->cbPCMBuf), &cRead);
005de8319b7bbb65252f4ecad4b4b8f650908d4dvboxsync if ( !cRead
605036d491298181444650ae12453c9207a7cf01vboxsync || RT_FAILURE(rc))
605036d491298181444650ae12453c9207a7cf01vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
605036d491298181444650ae12453c9207a7cf01vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync cbRead = AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead);
005de8319b7bbb65252f4ecad4b4b8f650908d4dvboxsync if (pa_stream_write(pThisStrmOut->pStream, pThisStrmOut->pvPCMBuf, cbRead, NULL /* Cleanup callback */,
605036d491298181444650ae12453c9207a7cf01vboxsync 0, PA_SEEK_RELATIVE) < 0)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = drvHostPulseAudioError(pThisStrmOut->pDrv, "Failed to write to output stream");
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync Assert(cbToRead >= cRead);
605036d491298181444650ae12453c9207a7cf01vboxsync cbToRead -= cbRead;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync cbReadTotal += cbRead;
605036d491298181444650ae12453c9207a7cf01vboxsync
005de8319b7bbb65252f4ecad4b4b8f650908d4dvboxsync LogFlowFunc(("\tcRead=%RU32 (%zu bytes) cbReadTotal=%RU32, cbToRead=%RU32\n",
605036d491298181444650ae12453c9207a7cf01vboxsync cRead, AUDIOMIXBUF_S2B(&pHstStrmOut->MixBuf, cRead), cbReadTotal, cbToRead));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync } while (0);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_SUCCESS(rc))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pHstStrmOut->MixBuf, cbReadTotal);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (cReadTotal)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync audioMixBufFinish(&pHstStrmOut->MixBuf, cReadTotal);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pcSamplesPlayed)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync *pcSamplesPlayed = cReadTotal;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
605036d491298181444650ae12453c9207a7cf01vboxsync LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n", cReadTotal, cbReadTotal, rc));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFuncLeaveRC(rc);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/** @todo Implement va handling. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic int drvHostPulseAudioError(PDRVHOSTPULSEAUDIO pThis, const char *szMsg)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pThis, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(szMsg, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pThis->cLogErrors++ < VBOX_PULSEAUDIO_MAX_LOG_REL_ERRORS)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync int rc2 = pa_context_errno(g_pContext);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("PulseAudio: %s: %s\n", szMsg, pa_strerror(rc2)));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /** @todo Implement some PulseAudio -> IPRT mapping here. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return VERR_GENERAL_FAILURE;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic DECLCALLBACK(int) drvHostPulseAudioFiniIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(pInterface);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFuncEnter();
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPULSEAUDIOSTREAM pThisStrmIn = (PPULSEAUDIOSTREAM)pHstStrmIn;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pThisStrmIn->pStream)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_lock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream_disconnect(pThisStrmIn->pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream_unref(pThisStrmIn->pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmIn->pStream = NULL;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return VINF_SUCCESS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic DECLCALLBACK(int) drvHostPulseAudioFiniOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMOUT pHstStrmOut)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(pInterface);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFuncEnter();
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPULSEAUDIOSTREAM pThisStrmOut = (PPULSEAUDIOSTREAM)pHstStrmOut;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pThisStrmOut->pStream)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_lock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream_disconnect(pThisStrmOut->pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream_unref(pThisStrmOut->pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmOut->pStream = NULL;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
605036d491298181444650ae12453c9207a7cf01vboxsync if (pThisStrmOut->pvPCMBuf)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
605036d491298181444650ae12453c9207a7cf01vboxsync RTMemFree(pThisStrmOut->pvPCMBuf);
605036d491298181444650ae12453c9207a7cf01vboxsync pThisStrmOut->pvPCMBuf = NULL;
605036d491298181444650ae12453c9207a7cf01vboxsync
605036d491298181444650ae12453c9207a7cf01vboxsync pThisStrmOut->cbPCMBuf = 0;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return VINF_SUCCESS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic DECLCALLBACK(int) drvHostPulseAudioControlOut(PPDMIHOSTAUDIO pInterface,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPDMAUDIOHSTSTRMOUT pHstStrmOut, PDMAUDIOSTREAMCMD enmStreamCmd)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(pInterface);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pHstStrmOut, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPULSEAUDIOSTREAM pThisStrmOut = (PPULSEAUDIOSTREAM)pHstStrmOut;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync int rc = VINF_SUCCESS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync switch (enmStreamCmd)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PDMAUDIOSTREAMCMD_ENABLE:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_lock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if ( pThisStrmOut->pDrainOp
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync && pa_operation_get_state(pThisStrmOut->pDrainOp) != PA_OPERATION_DONE)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_operation_cancel(pThisStrmOut->pDrainOp);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_operation_unref(pThisStrmOut->pDrainOp);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmOut->pDrainOp = NULL;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync else
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* This should return immediately. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = drvHostPulseAudioWaitFor(pa_stream_cork(pThisStrmOut->pStream, 0,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync drvHostPulseAudioCbSuccess, pThisStrmOut),
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync 15 * 1000 /* 15s timeout */);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PDMAUDIOSTREAMCMD_DISABLE:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* Pause audio output (the Pause bit of the AC97 x_CR register is set).
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * Note that we must return immediately from here! */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_lock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (!pThisStrmOut->pDrainOp)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* This should return immediately. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = drvHostPulseAudioWaitFor(pa_stream_trigger(pThisStrmOut->pStream,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync drvHostPulseAudioCbSuccess, pThisStrmOut),
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync 15 * 1000 /* 15s timeout */);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (RT_LIKELY(RT_SUCCESS(rc)))
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmOut->pDrainOp = pa_stream_drain(pThisStrmOut->pStream,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync drvHostPulseAudioCbStreamDrain, pThisStrmOut);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync default:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = VERR_INVALID_PARAMETER;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFuncLeaveRC(rc);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic DECLCALLBACK(int) drvHostPulseAudioControlIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOHSTSTRMIN pHstStrmIn,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDMAUDIOSTREAMCMD enmStreamCmd)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(pInterface);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pHstStrmIn, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PPULSEAUDIOSTREAM pThisStrmIn = (PPULSEAUDIOSTREAM)pHstStrmIn;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync int rc = VINF_SUCCESS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync switch (enmStreamCmd)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PDMAUDIOSTREAMCMD_ENABLE:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_lock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* This should return immediately. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = drvHostPulseAudioWaitFor(pa_stream_cork(pThisStrmIn->pStream, 0 /* Play / resume */,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync drvHostPulseAudioCbSuccess, pThisStrmIn),
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync 15 * 1000 /* 15s timeout */);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync case PDMAUDIOSTREAMCMD_DISABLE:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_lock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (pThisStrmIn->pu8PeekBuf) /* Do we need to drop the peek buffer?*/
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_stream_drop(pThisStrmIn->pStream);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThisStrmIn->pu8PeekBuf = NULL;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* This should return immediately. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = drvHostPulseAudioWaitFor(pa_stream_cork(pThisStrmIn->pStream, 1 /* Stop / pause */,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync drvHostPulseAudioCbSuccess, pThisStrmIn),
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync 15 * 1000 /* 15s timeout */);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_unlock(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync default:
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync rc = VERR_INVALID_PARAMETER;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync break;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return rc;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic DECLCALLBACK(int) drvHostPulseAudioGetConf(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pCfg)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NOREF(pInterface);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pCfg->cbStreamOut = sizeof(PULSEAUDIOSTREAM);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pCfg->cbStreamIn = sizeof(PULSEAUDIOSTREAM);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pCfg->cMaxHstStrmsOut = INT_MAX;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pCfg->cMaxHstStrmsIn = INT_MAX;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return VINF_SUCCESS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsyncstatic DECLCALLBACK(void) drvHostPulseAudioShutdown(PPDMIHOSTAUDIO pInterface)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync NOREF(pInterface);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFuncEnter();
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (g_pMainLoop)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_stop(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (g_pContext)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_context_disconnect(g_pContext);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_context_unref(g_pContext);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync g_pContext = NULL;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync if (g_pMainLoop)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pa_threaded_mainloop_free(g_pMainLoop);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync g_pMainLoop = NULL;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync }
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogFlowFuncLeave();
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/**
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync */
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsyncstatic DECLCALLBACK(void *) drvHostPulseAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync{
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync AssertPtrReturn(pInterface, NULL);
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync AssertPtrReturn(pszIID, NULL);
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync return NULL;
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync}
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync/**
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync * Constructs a PulseAudio Audio driver instance.
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync *
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * @copydoc FNPDMDRVCONSTRUCT
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic DECLCALLBACK(int) drvHostPulseAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync AssertPtrReturn(pDrvIns, VERR_INVALID_POINTER);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pCfg is optional. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDRVHOSTPULSEAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTPULSEAUDIO);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync LogRel(("Audio: Initializing PulseAudio driver\n"));
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pThis->pDrvIns = pDrvIns;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* IBase */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync pDrvIns->IBase.pfnQueryInterface = drvHostPulseAudioQueryInterface;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* IHostAudio */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostPulseAudio);
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync return VINF_SUCCESS;
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync}
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync/**
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync * Destructs a PulseAudio Audio driver instance.
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync *
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync * @copydoc FNPDMDRVCONSTRUCT
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync */
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsyncstatic DECLCALLBACK(void) drvHostPulseAudioDestruct(PPDMDRVINS pDrvIns)
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync{
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync NOREF(pDrvIns);
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync LogFlowFuncEnter();
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync}
1705f7565ed8533058b8541d72d6c5d4453de00fvboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync/**
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync * Char driver registration record.
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncconst PDMDRVREG g_DrvHostPulseAudio =
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* u32Version */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDM_DRVREG_VERSION,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* szName */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync "PulseAudio",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* szRCMod */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync "",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* szR0Mod */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync "",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pszDescription */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync "Pulse Audio host driver",
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* fFlags */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* fClass. */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDM_DRVREG_CLASS_AUDIO,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* cMaxInstances */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync ~0U,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* cbInstance */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync sizeof(DRVHOSTPULSEAUDIO),
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pfnConstruct */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync drvHostPulseAudioConstruct,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pfnDestruct */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync drvHostPulseAudioDestruct,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pfnRelocate */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NULL,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pfnIOCtl */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NULL,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pfnPowerOn */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NULL,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pfnReset */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NULL,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pfnSuspend */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NULL,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pfnResume */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NULL,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pfnAttach */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NULL,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pfnDetach */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NULL,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pfnPowerOff */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NULL,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* pfnSoftReset */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NULL,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync /* u32EndVersion */
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync PDM_DRVREG_VERSION
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync};
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsyncstatic struct audio_option pulse_options[] =
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync{
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {"DAC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_out,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync "DAC period size in milliseconds", NULL, 0},
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync {"ADC_MS", AUD_OPT_INT, &s_pulseCfg.buffer_msecs_in,
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync "ADC period size in milliseconds", NULL, 0},
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync NULL
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync};
7da9e7e719adde3baba3f6fa1d0bcfb170cf9911vboxsync