pulseaudio.c revision a72701e7ed0431c8c350c23b27d9d7d4d739bc24
/** @file
*
* VBox PulseAudio backend
*/
/*
* Copyright (C) 2006-2007 innotek GmbH
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DEV_AUDIO
#include <pulse/pulseaudio.h>
#include "pulse_stubs.h"
#include "../../vl_vbox.h"
#include "audio.h"
#define AUDIO_CAP "pulse"
#include "audio_int.h"
#include <stdio.h>
/*
* We use a g_pMainLoop in a separate thread g_pContext. We have to call functions for
* manipulating objects either from callback functions or we have to protect
* these functions by pa_threaded_mainloop_lock() / pa_threaded_mainloop_unlock().
*/
static struct pa_threaded_mainloop *g_pMainLoop;
static struct pa_context *g_pContext;
static void pulse_audio_fini (void *);
typedef struct PulseVoice
{
void *pPCMBuf;
int fOpSuccess;
} PulseVoice;
static struct
{
int buffer_msecs_out;
int buffer_msecs_in;
} conf
=
{
};
struct pulse_params_req
{
int freq;
int nchannels;
};
struct pulse_params_obt
{
int freq;
int nchannels;
unsigned long buffer_size;
};
{
switch (fmt)
{
case AUD_FMT_U8:
return PA_SAMPLE_U8;
case AUD_FMT_S16:
return PA_SAMPLE_S16LE;
#ifdef PA_SAMPLE_S32LE
case AUD_FMT_S32:
return PA_SAMPLE_S32LE;
#endif
default:
return PA_SAMPLE_U8;
}
}
{
switch (pulsefmt)
{
case PA_SAMPLE_U8:
*endianess = 0;
*fmt = AUD_FMT_U8;
break;
case PA_SAMPLE_S16LE:
*fmt = AUD_FMT_S16;
*endianess = 0;
break;
case PA_SAMPLE_S16BE:
*fmt = AUD_FMT_S16;
*endianess = 1;
break;
#ifdef PA_SAMPLE_S32LE
case PA_SAMPLE_S32LE:
*fmt = AUD_FMT_S32;
*endianess = 0;
break;
#endif
#ifdef PA_SAMPLE_S32BE
case PA_SAMPLE_S32BE:
*fmt = AUD_FMT_S32;
*endianess = 1;
break;
#endif
default:
return -1;
}
return 0;
}
{
switch (pa_context_get_state(c))
{
case PA_CONTEXT_READY:
case PA_CONTEXT_TERMINATED:
case PA_CONTEXT_FAILED:
break;
default:
break;
}
}
{
switch (pa_stream_get_state(s))
{
case PA_STREAM_READY:
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
break;
default:
break;
}
}
{
}
{
const pa_buffer_attr *pBufAttr;
const pa_sample_spec *pSampSpec;
LogRel(("Pulse: open %s rate=%dHz channels=%d format=%s\n",
if (!pa_sample_spec_valid(&sspec))
{
LogRel(("Pulse: Unsupported sample specification\n"));
goto fail;
}
#if 0
#endif
{
goto unlock_and_fail;
}
if (fIn)
{
{
LogRel(("Pulse: Cannot connect record stream : %s\n",
}
}
else
{
{
LogRel(("Pulse: Cannot connect playback stream: %s\n",
}
}
/* Wait until the stream is ready */
{
}
LogRel(("Pulse: buffer settings: max=%d tlength=%d prebuf=%d minreq=%d\n",
return 0;
fail:
if (pStream)
return -1;
}
{
struct pulse_params_req req;
struct pulse_params_obt obt;
int endianness;
return -1;
{
return -1;
}
{
return -1;
}
return 0;
}
{
{
}
{
}
}
{
if (!csLive)
return 0;
if (cbAvail == -1)
{
LogRel(("Pulse: Failed to determine the writable size: %s\n",
return 0;
}
while (csSamples)
{
/* split request at the end of our samples buffer */
{
LogRel(("Pulse: Failed to write %d samples: %s\n",
break;
}
}
return csDecr;
}
{
}
{
}
typedef enum
{
Unpause = 0,
Pause = 1,
Flush = 2,
Trigger = 3
} pulse_cmd_t;
{
return 0;
switch (cmd)
{
case Pause:
break;
case Unpause:
break;
case Flush:
break;
case Trigger:
break;
default:
goto fail;
}
if (!op)
LogRel(("Pulse: Failed ctrl cmd=%d to stream: %s\n",
else
fail:
return 0;
}
{
switch (cmd)
{
case VOICE_ENABLE:
break;
case VOICE_DISABLE:
break;
default:
return -1;
}
return 0;
}
{
struct pulse_params_req req;
struct pulse_params_obt obt;
int endianness;
return -1;
{
return -1;
}
/* pcm_in: reserve twice as the maximum buffer length because of peek()/drop(). */
/* no buffer for input */
return 0;
}
{
{
}
{
}
}
{
const void *pu8Src;
if (!csDead)
return 0; /* no buffer available */
{
LogRel(("Pulse: Peek failed: %s\n",
goto exit;
}
if (!pu8Src)
goto exit;
while (csSamples)
{
/* split request at the end of our samples buffer */
}
exit:
return csDecr;
}
{
}
{
return 0;
}
static void *pulse_audio_init (void)
{
int rc;
rc = audioLoadPulseLib();
if (RT_FAILURE(rc))
{
return NULL;
}
if (!(g_pMainLoop = pa_threaded_mainloop_new()))
{
LogRel(("Pulse: Failed to allocate main loop: %s\n",
goto fail;
}
{
LogRel(("Pulse: Failed to allocate context: %s\n",
goto fail;
}
if (pa_threaded_mainloop_start(g_pMainLoop) < 0)
{
LogRel(("Pulse: Failed to start threaded mainloop: %s\n",
goto fail;
}
{
LogRel(("Pulse: Failed to connect to server: %s\n",
goto fail;
}
/* Wait until the g_pContext is ready */
{
goto unlock_and_fail;
}
return &conf;
if (g_pMainLoop)
fail:
if (g_pMainLoop)
if (g_pContext)
{
g_pContext = NULL;
}
if (g_pMainLoop)
{
g_pMainLoop = NULL;
}
return NULL;
}
static void pulse_audio_fini (void *opaque)
{
if (g_pMainLoop)
if (g_pContext)
{
g_pContext = NULL;
}
if (g_pMainLoop)
{
g_pMainLoop = NULL;
}
(void) opaque;
}
static struct audio_option pulse_options[] =
{
"DAC period size in milliseconds", NULL, 0},
"ADC period size in milliseconds", NULL, 0},
};
static struct audio_pcm_ops pulse_pcm_ops =
{
};
struct audio_driver pulse_audio_driver =
{
};