audio.c revision 2bce97064c3a5a1dba52a6e0b76c8282ca7b4c5e
/*
* QEMU Audio subsystem
*
* Copyright (c) 2003-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#define LOG_GROUP LOG_GROUP_DEV_AUDIO
#include "Builtins.h"
#include "../../vl_vbox.h"
#include <ctype.h>
#include <stdlib.h>
#define AUDIO_CAP "audio"
#include "audio.h"
#include "audio_int.h"
#ifdef RT_OS_WINDOWS
#define strcasecmp stricmp
#endif
/* #define DEBUG_PLIVE */
/* #define DEBUG_LIVE */
/* #define DEBUG_OUT */
/* #define DEBUG_CAPTURE */
typedef struct DRVAUDIO
{
/** The audio interface. */
/** Pointer to the driver instance. */
static struct audio_driver *drvtab[] = {
#ifdef RT_OS_LINUX
#ifdef VBOX_WITH_ALSA
#endif
#endif
#ifdef RT_OS_DARWIN
#endif
#ifdef RT_OS_WINDOWS
#endif
#ifdef RT_OS_L4
#endif
};
struct fixed_settings {
int enabled;
int nb_voices;
int greedy;
};
static struct {
struct fixed_settings fixed_out;
struct fixed_settings fixed_in;
union {
int hz;
} period;
int plive;
} conf = {
{ /* DAC fixed settings */
1, /* enabled */
1, /* nb_voices */
1, /* greedy */
{
44100, /* freq */
2, /* nchannels */
AUD_FMT_S16 /* fmt */
}
},
{ /* ADC fixed settings */
1, /* enabled */
1, /* nb_voices */
1, /* greedy */
{
44100, /* freq */
2, /* nchannels */
AUD_FMT_S16 /* fmt */
}
},
{ 100 }, /* period */
0, /* plive */
};
static AudioState glob_audio_state;
0,
#ifdef FLOAT_MIXENG
1.0,
1.0
#else
#ifndef VBOX
#else
#endif
#endif
};
#ifdef VBOX
{
0,
};
{
0,
};
#endif
{
u = ((u&0x55555555) + ((u>>1)&0x55555555));
u = ((u&0x33333333) + ((u>>2)&0x33333333));
u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
u = ( u&0x0000ffff) + (u>>16);
return u;
}
{
return popcount ((u&-u)-1);
}
uint64_t audio_get_clock (void)
{
AudioState *s;
s = &glob_audio_state;
}
uint64_t audio_get_ticks_per_sec (void)
{
AudioState *s;
s = &glob_audio_state;
}
#else
{
if (cond) {
static int shown;
if (!shown) {
shown = 1;
}
#if defined AUDIO_BREAKPOINT_ON_BUG
# if defined HOST_I386
# if defined __GNUC__
__asm__ ("int3");
# else
abort ();
# endif
# else
abort ();
# endif
#endif
}
return cond;
}
#endif
{
int cond;
funcname);
return NULL;
}
return qemu_mallocz (len);
}
{
switch (fmt) {
case AUD_FMT_U8:
return "U8";
case AUD_FMT_U16:
return "U16";
case AUD_FMT_S8:
return "S8";
case AUD_FMT_S16:
return "S16";
}
return "S16";
}
int *defaultp)
{
if (!strcasecmp (s, "u8")) {
*defaultp = 0;
return AUD_FMT_U8;
}
else if (!strcasecmp (s, "u16")) {
*defaultp = 0;
return AUD_FMT_U16;
}
else if (!strcasecmp (s, "s8")) {
*defaultp = 0;
return AUD_FMT_S8;
}
else if (!strcasecmp (s, "s16")) {
*defaultp = 0;
return AUD_FMT_S16;
}
else {
dolog ("Bogus audio format `%s' using %s\n",
s, audio_audfmt_to_string (defval));
*defaultp = 1;
return defval;
}
}
int *defaultp)
{
if (!var) {
*defaultp = 1;
return defval;
}
}
{
int val;
char *strval;
if (strval) {
*defaultp = 0;
return val;
}
else {
*defaultp = 1;
return defval;
}
}
static const char *audio_get_conf_str (const char *key,
const char *defval,
int *defaultp)
{
if (!val) {
*defaultp = 1;
return defval;
}
else {
*defaultp = 0;
return val;
}
}
{
if (cap) {
}
else {
}
}
{
}
static void audio_process_options (const char *prefix,
struct audio_option *opt)
{
char *optname;
const char vbox_prefix[] = "VBOX_";
dolog ("prefix = NULL\n");
return;
}
dolog ("opt = NULL\n");
return;
}
int def;
dolog ("Option value pointer for `%s' is not set\n",
continue;
}
/* len of opt->name + len of prefix + size of vbox_prefix
* (includes trailing zero) + zero + underscore (on behalf of
* sizeof) */
if (!optname) {
dolog ("Could not allocate memory for option name `%s'\n",
continue;
}
/* copy while upcasing, including trailing zero */
for (i = 0; i <= preflen; ++i) {
}
def = 1;
case AUD_OPT_BOOL:
case AUD_OPT_INT:
{
}
break;
case AUD_OPT_FMT:
{
}
break;
case AUD_OPT_STR:
{
}
break;
default:
dolog ("Bad value tag for option `%s' - %d\n",
break;
}
if (!opt->overridenp) {
}
}
}
{
case AUD_FMT_S8:
break;
case AUD_FMT_U8:
break;
case AUD_FMT_S16:
break;
case AUD_FMT_U16:
break;
default:
break;
}
switch (as->endianness) {
case 0:
break;
case 1:
break;
default:
break;
}
}
{
int invalid;
case AUD_FMT_S8:
case AUD_FMT_U8:
case AUD_FMT_S16:
case AUD_FMT_U16:
break;
default:
invalid = 1;
break;
}
return invalid ? -1 : 0;
}
{
case AUD_FMT_S8:
sign = 1;
case AUD_FMT_U8:
break;
case AUD_FMT_S16:
sign = 1;
case AUD_FMT_U16:
bits = 16;
break;
}
}
{
case AUD_FMT_S8:
sign = 1;
case AUD_FMT_U8:
break;
case AUD_FMT_S16:
sign = 1;
case AUD_FMT_U16:
bits = 16;
break;
}
}
{
if (!len) {
return;
}
}
else {
}
else {
int i;
short s = INT16_MAX;
if (info->swap_endianness) {
s = bswap16 (s);
}
p[i] = s;
}
}
}
}
/*
* Capture
*/
{
(void) src;
(void) dst;
(void) samples;
(void) vol;
}
AudioState *s,
)
{
return cap;
}
}
return NULL;
}
{
struct capture_callback *cb;
#ifdef DEBUG_CAPTURE
#endif
}
}
{
}
}
{
SWVoiceOut *sw;
int enabled = 0;
enabled = 1;
break;
}
}
}
{
while (sc) {
}
if (was_active) {
/* We have removed soft voice from the capture:
this might have changed the overall status of the capture
since this might have been the only active voice */
}
}
}
{
SWVoiceCap *sc;
SWVoiceOut *sw;
if (!sc) {
dolog ("Could not allocate soft capture voice (%u bytes)\n",
sizeof (*sc));
return -1;
}
return -1;
}
#ifdef DEBUG_CAPTURE
#endif
}
}
return 0;
}
/*
* Hard voice (capture)
*/
{
int m = hw->total_samples_captured;
}
}
return m;
}
{
return 0;
}
return live;
}
/*
* Soft voice (capture)
*/
{
int rpos;
return 0;
}
if (rpos >= 0) {
return rpos;
}
else {
}
}
{
return 0;
}
if (!live) {
return 0;
}
while (swlim) {
/* XXX: <= ? */
if (isamp <= 0) {
}
if (!isamp) {
break;
}
return 0;
}
}
}
/*
* Hard voice (playback)
*/
{
SWVoiceOut *sw;
int m = INT_MAX;
int nb_live = 0;
nb_live += 1;
}
}
return m;
}
{
int smin;
if (!*nb_live) {
return 0;
}
else {
return 0;
}
return live;
}
}
{
int nb_live;
int live;
return 0;
}
return live;
}
/*
* Soft voice (playback)
*/
{
if (!sw) {
return size;
}
return 0;
}
#ifdef DEBUG_OUT
#endif
return 0;
}
if (swlim) {
#ifndef VBOX
#else
#endif
}
while (swlim) {
if (!blck) {
break;
}
&isamp,
);
}
#ifdef DEBUG_OUT
dolog (
"%s: write size %d ret %d total sw %d\n",
ret,
);
#endif
}
#ifdef DEBUG_AUDIO
{
dolog ("%s: bits %d, sign %d, freq %d, nchan %d\n",
}
#endif
#define DAC
#include "audio_template.h"
#include "audio_template.h"
{
int bytes;
if (!sw) {
/* XXX: Consider options */
return size;
}
return 0;
}
return bytes;
}
{
int bytes;
if (!sw) {
/* XXX: Consider options */
return size;
}
return 0;
}
return bytes;
}
{
}
{
HWVoiceOut *hw;
if (!sw) {
return;
}
SWVoiceCap *sc;
if (on) {
hw->pending_disable = 0;
}
}
else {
int nb_active = 0;
}
}
}
}
}
}
}
{
if (!sw) {
return;
}
if (on) {
}
}
else {
int nb_active = 0;
}
if (nb_active == 1) {
}
}
}
}
}
{
int live;
if (!sw) {
return 0;
}
return 0;
}
ldebug (
"%s: get_avail live %d ret %lld\n",
);
}
{
if (!sw) {
return 0;
}
return 0;
}
#ifdef DEBUG_OUT
dolog ("%s: get_free live %d dead %d ret %lld\n",
#endif
}
{
int n;
SWVoiceCap *sc;
n = samples;
while (n) {
int written;
dolog ("Could not mix %d bytes into a capture "
"buffer, mixed %d\n",
break;
}
n -= to_write;
}
}
}
}
static void audio_run_out (AudioState *s)
{
SWVoiceOut *sw;
int played;
if (!nb_live) {
live = 0;
}
continue;
}
SWVoiceCap *sc;
#ifdef DEBUG_OUT
dolog ("Disabling voice\n");
#endif
hw->pending_disable = 0;
}
continue;
}
if (!live) {
if (free > 0) {
}
}
}
continue;
}
dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
}
#ifdef DEBUG_OUT
#endif
if (played) {
}
cleanup_required = 0;
continue;
}
dolog ("played=%d sw->total_hw_samples_mixed=%d\n",
}
if (!sw->total_hw_samples_mixed) {
}
if (free > 0) {
}
}
}
if (cleanup_required) {
while (sw) {
#ifdef DEBUG_PLIVE
dolog ("Finishing with old voice\n");
#endif
audio_close_out (s, sw);
}
}
}
}
}
static void audio_run_in (AudioState *s)
{
int avail;
if (avail > 0) {
}
}
}
}
}
static void audio_run_capture (AudioState *s)
{
SWVoiceOut *sw;
while (live) {
struct capture_callback *cb;
}
live -= to_capture;
}
continue;
}
dolog ("captured=%d sw->total_hw_samples_mixed=%d\n",
}
}
}
}
static void audio_timer (void *opaque)
{
AudioState *s = opaque;
audio_run_out (s);
audio_run_in (s);
audio_run_capture (s);
}
static struct audio_option audio_options[] = {
/* DAC */
"Use fixed settings for host DAC", NULL, 0},
"Frequency for fixed host DAC", NULL, 0},
"Format for fixed host DAC", NULL, 0},
"Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0},
"Number of voices for DAC", NULL, 0},
/* ADC */
"Use fixed settings for host ADC", NULL, 0},
"Frequency for fixed host ADC", NULL, 0},
"Format for fixed host ADC", NULL, 0},
"Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0},
"Number of voices for ADC", NULL, 0},
/* Misc */
"Timer period in HZ (0 - use lowest possible)", NULL, 0},
"(undocumented)", NULL, 0},
};
{
}
if (s->drv_opaque) {
audio_init_nb_voices_out (s, drv);
audio_init_nb_voices_in (s, drv);
return 0;
}
else {
return -1;
}
}
{
AudioState *s = opaque;
}
}
}
static void audio_atexit (void)
{
AudioState *s = &glob_audio_state;
SWVoiceCap *sc;
struct capture_callback *cb;
}
}
}
}
if (s->drv) {
}
}
{
AudioState *s = &glob_audio_state;
}
{
}
{
AudioState *s = &glob_audio_state;
audio_timer (s);
}
{
size_t i;
int done = 0;
AudioState *s = &glob_audio_state;
int rc;
LIST_INIT (&s->hw_head_out);
LIST_INIT (&s->hw_head_in);
if (VBOX_FAILURE (rc))
return rc;
if (s->nb_hw_voices_out <= 0) {
dolog ("Bogus number of playback voices %d, setting to 1\n",
s->nb_hw_voices_out);
s->nb_hw_voices_out = 1;
}
if (s->nb_hw_voices_in <= 0) {
dolog ("Bogus number of capture voices %d, setting to 0\n",
s->nb_hw_voices_in);
s->nb_hw_voices_in = 0;
}
if (drvname) {
int found = 0;
found = 1;
break;
}
}
if (!found) {
}
}
if (!done) {
if (drvtab[i]->can_be_default) {
LogRel(("Audio: Initialization of driver '%s' failed, trying '%s'.\n",
}
}
}
if (!done) {
if (!done) {
dolog ("Could not initialize audio subsystem\n");
}
else {
dolog ("warning: Using timer based audio emulation\n");
}
}
if (done) {
dolog ("warning: Timer period is negative - %d "
"treating as zero\n",
}
}
else {
}
}
else {
/* XXX */
return rc;
}
return VINF_SUCCESS;
}
int AUD_init_null(void)
{
AudioState *s = &glob_audio_state;
return audio_driver_init (s, &no_audio_driver);
}
AudioState *s,
struct audio_capture_ops *ops,
void *cb_opaque
)
{
struct capture_callback *cb;
if (!s) {
/* XXX suppress */
s = &glob_audio_state;
}
if (audio_validate_settings (as)) {
dolog ("Invalid settings were passed when trying to add capture\n");
goto err0;
}
if (!cb) {
dolog ("Could not allocate capture callback information, size %u\n",
sizeof (*cb));
goto err0;
}
if (cap) {
return cap;
}
else {
HWVoiceOut *hw;
if (!cap) {
dolog ("Could not allocate capture voice, size %u\n",
sizeof (*cap));
goto err1;
}
/* XXX find a more elegant way */
sizeof (st_sample_t));
dolog ("Could not allocate capture mix buffer (%d samples)\n",
goto err2;
}
dolog ("Could not allocate capture buffer "
"(%d samples, each %d bytes)\n",
goto err3;
}
audio_attach_capture (s, hw);
}
return cap;
err3:
err2:
err1:
err0:
return NULL;
}
}
{
struct capture_callback *cb;
while (sw) {
#ifdef DEBUG_CAPTURE
#endif
}
}
}
return;
}
}
}
{
if (sw)
{
}
}
{
const char *name;
switch (mt)
{
case AUD_MIXER_VOLUME:
name = "MASTER";
vol = &pcm_out_volume;
break;
case AUD_MIXER_PCM:
name = "PCM_OUT";
break;
case AUD_MIXER_LINE_IN:
name = "LINE_IN";
vol = &pcm_in_volume;
break;
default:
return;
}
if (vol)
{
}
#if 0
#endif
}
{
}
/**
* Queries an interface to the driver.
*
* @returns Pointer to interface.
* @returns NULL if the interface was not supported by the driver.
* @param pInterface Pointer to this interface structure.
* @param enmInterface The requested interface identification.
* @thread Any thread.
*/
{
switch (enmInterface)
{
case PDMINTERFACE_BASE:
return &pData->IAudioConnector;
default:
return NULL;
}
}
/**
* Power Off notification.
*
* @param pDrvIns The driver instance data.
*/
{
AudioState *s = &glob_audio_state;
audio_vm_change_state_handler (s, 0);
}
/**
* Destruct a driver instance.
*
* Most VM resources are freed by the VM. This callback is provided so that any non-VM
* resources can be freed correctly.
*
* @param pDrvIns The driver instance data.
*/
{
LogFlow(("drvAUDIODestruct:\n"));
audio_atexit ();
}
/**
* Construct an AUDIO driver instance.
*
* @returns VBox status.
* @param pDrvIns The driver instance data.
* If the registration structure is needed, pDrvIns->pDrvReg points to it.
* @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration
* of the driver instance. It's also found in pDrvIns->pCfgHandle, but like
* iInstance it's expected to be used a bit in this function.
*/
{
int rc;
char *drvname;
LogFlow(("drvAUDIOConstruct:\n"));
/*
* Validate the config.
*/
/*
* Init the static parts.
*/
/* IBase */
/* IAudio */
/* pData->IAudioConnector.pfn; */
if (VBOX_FAILURE (rc))
return rc;
if (VBOX_FAILURE (rc))
return rc;
return VINF_SUCCESS;
}
/**
* Suspend notification.
*
* @returns VBox status.
* @param pDrvIns The driver instance data.
*/
{
AudioState *s = &glob_audio_state;
audio_vm_change_state_handler (s, 0);
}
/**
* Resume notification.
*
* @returns VBox status.
* @param pDrvIns The driver instance data.
*/
{
AudioState *s = &glob_audio_state;
audio_vm_change_state_handler (s, 1);
}
/**
* Audio driver registration record.
*/
const PDMDRVREG g_DrvAUDIO =
{
/* u32Version */
/* szDriverName */
"AUDIO",
/* pszDescription */
"AUDIO Driver",
/* fFlags */
/* fClass. */
/* cMaxInstances */
1,
/* cbInstance */
sizeof(DRVAUDIO),
/* pfnConstruct */
/* pfnDestruct */
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
NULL,
/* pfnSuspend */
/* pfnResume */
/* pfnDetach */
NULL,
/* pfnPowerOff */
};