alsaaudio.c revision da34f630cce680921b71c5f024533113106d4f04
/*
* QEMU ALSA audio driver
*
* Copyright (c) 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.
*/
#ifdef VBOX
#ifndef DEBUG
#define NDEBUG
#endif
#define LOG_GROUP LOG_GROUP_DEV_AUDIO
#endif
#include <alsa/asoundlib.h>
#include "Builtins.h"
#include "../../vl_vbox.h"
#include "audio.h"
#ifdef VBOX
#include "alsa_stubs.h"
#endif
#define AUDIO_CAP "alsa"
#include "audio_int.h"
typedef struct ALSAVoiceOut {
void *pcm_buf;
} ALSAVoiceOut;
typedef struct ALSAVoiceIn {
void *pcm_buf;
} ALSAVoiceIn;
/* latency = period_size * periods / (rate * bytes_per_frame) */
static struct {
int size_in_usec_in;
int size_in_usec_out;
const char *pcm_name_in;
const char *pcm_name_out;
unsigned int buffer_size_in;
unsigned int period_size_in;
unsigned int buffer_size_out;
unsigned int period_size_out;
unsigned int threshold;
int verbose;
} conf = {
#ifdef HIGH_LATENCY
#else
INIT_FIELD (.size_in_usec_in =) 0,
INIT_FIELD (.size_in_usec_out =) 0,
#endif
#ifdef HIGH_LATENCY
#else
#define DEFAULT_BUFFER_SIZE 1024
#define DEFAULT_PERIOD_SIZE 256
#endif
INIT_FIELD (.threshold =) 0,
INIT_FIELD (.buffer_size_in_overriden =) 0,
INIT_FIELD (.period_size_in_overriden =) 0,
INIT_FIELD (.buffer_size_out_overriden =) 0,
INIT_FIELD (.period_size_out_overriden =) 0,
INIT_FIELD (.verbose =) 0
};
struct alsa_params_req {
int freq;
int nchannels;
unsigned long buffer_size;
unsigned long period_size;
};
struct alsa_params_obt {
int freq;
int nchannels;
};
{
}
int err,
const char *typ,
const char *fmt,
...
)
{
}
{
if (err) {
(void *) *handlep);
}
}
{
}
{
switch (fmt) {
case AUD_FMT_S8:
return SND_PCM_FORMAT_S8;
case AUD_FMT_U8:
return SND_PCM_FORMAT_U8;
case AUD_FMT_S16:
return SND_PCM_FORMAT_S16_LE;
case AUD_FMT_U16:
return SND_PCM_FORMAT_U16_LE;
case AUD_FMT_S32:
return SND_PCM_FORMAT_S32_LE;
case AUD_FMT_U32:
return SND_PCM_FORMAT_U32_LE;
default:
#ifdef DEBUG_AUDIO
abort ();
#endif
return SND_PCM_FORMAT_U8;
}
}
{
switch (alsafmt) {
case SND_PCM_FORMAT_S8:
*endianness = 0;
*fmt = AUD_FMT_S8;
break;
case SND_PCM_FORMAT_U8:
*endianness = 0;
*fmt = AUD_FMT_U8;
break;
case SND_PCM_FORMAT_S16_LE:
*endianness = 0;
*fmt = AUD_FMT_S16;
break;
case SND_PCM_FORMAT_U16_LE:
*endianness = 0;
*fmt = AUD_FMT_U16;
break;
case SND_PCM_FORMAT_S16_BE:
*endianness = 1;
*fmt = AUD_FMT_S16;
break;
case SND_PCM_FORMAT_U16_BE:
*endianness = 1;
*fmt = AUD_FMT_U16;
break;
case SND_PCM_FORMAT_S32_LE:
*endianness = 0;
*fmt = AUD_FMT_S32;
break;
case SND_PCM_FORMAT_U32_LE:
*endianness = 0;
*fmt = AUD_FMT_U32;
break;
case SND_PCM_FORMAT_S32_BE:
*endianness = 1;
*fmt = AUD_FMT_S32;
break;
case SND_PCM_FORMAT_U32_BE:
*endianness = 1;
*fmt = AUD_FMT_U32;
break;
default:
return -1;
}
return 0;
}
#if defined DEBUG_MISMATCHES || defined DEBUG
struct alsa_params_obt *obt)
{
dolog ("parameter | requested value | obtained value\n");
dolog ("channels | %10d | %10d\n",
dolog ("============================================\n");
dolog ("requested: buffer size %d period size %d\n",
}
#endif
{
int err;
if (err < 0) {
dolog ("Could not fully initialize DAC\n");
return;
}
if (err < 0) {
dolog ("Could not fully initialize DAC\n");
return;
}
if (err < 0) {
dolog ("Could not fully initialize DAC\n");
return;
}
}
{
unsigned int period_size, buffer_size;
err = snd_pcm_open (
&handle,
);
if (err < 0) {
#ifndef VBOX
#else
#endif
return -1;
}
if (err < 0) {
#ifndef VBOX
#else
LogRel(("ALSA: Failed to initialize hardware parameters\n"));
#endif
goto err;
}
);
if (err < 0) {
#ifndef VBOX
#else
LogRel(("ALSA: Failed to set access type\n"));
#endif
goto err;
}
if (err < 0) {
#ifndef VBOX
#else
#endif
goto err;
}
if (err < 0) {
#ifndef VBOX
#else
#endif
goto err;
}
);
if (err < 0) {
#ifndef VBOX
#else
#endif
goto err;
}
#ifndef VBOX
"Can not handle obtained number of channels %d\n",
#else
#endif
goto err;
}
if (!buffer_size) {
}
}
if (buffer_size) {
if (period_size) {
0
);
if (err < 0) {
#ifndef VBOX
"Failed to set period time %d\n",
req->period_size);
#else
#endif
goto err;
}
}
0
);
if (err < 0) {
#ifndef VBOX
"Failed to set buffer time %d\n",
req->buffer_size);
#else
#endif
goto err;
}
}
else {
if (period_size_f) {
dir = 0;
&minval,
&dir
);
if (err < 0) {
#ifndef VBOX
err,
"Could not get minimal period size for %s\n",
);
#else
#endif
}
else {
if (period_size_f < minval) {
dolog ("%s period size(%d) is less "
"than minimal period size(%ld)\n",
typ,
minval);
}
}
}
#ifndef VBOX
0
);
#else
0
);
#endif
if (err < 0) {
#ifndef VBOX
#else
LogRel(("ALSA: Failed to set period size %d (%s)\n",
#endif
goto err;
}
}
#ifdef VBOX
/* Calculate default buffer size here since it might have been changed
* in the _near functions */
#endif
);
if (err < 0) {
#ifndef VBOX
typ);
#else
#endif
}
else {
if (buffer_size_f < minval) {
dolog (
"%s buffer size(%d) is less "
"than minimal buffer size(%ld)\n",
typ,
);
}
}
}
);
if (err < 0) {
#ifndef VBOX
#else
LogRel(("ALSA: Failed to set buffer size %d (%s)\n",
#endif
goto err;
}
}
}
else {
dolog ("warning: Buffer size is not set\n");
}
if (err < 0) {
#ifndef VBOX
#else
LogRel(("ALSA: Failed to apply audio parameters\n"));
#endif
goto err;
}
if (err < 0) {
#ifndef VBOX
#else
LogRel(("ALSA: Failed to get buffer size\n"));
#endif
goto err;
}
#ifdef VBOX
dir = 0;
if (err < 0)
{
LogRel(("ALSA: Failed to get period size\n"));
goto err;
}
LogRel(("ALSA: %s frequency %dHz, period size %ld, buffer size %ld\n",
#endif
if (err < 0) {
(void *) handle);
goto err;
}
int bytes_per_sec;
<< (nchannels == 2)
}
#if defined DEBUG_MISMATCHES || defined DEBUG
}
#endif
#ifdef DEBUG
#endif
return 0;
err:
return -1;
}
{
if (err < 0) {
(void *) handle);
return -1;
}
return 0;
}
{
if (err < 0) {
#ifndef VBOX
#endif
return -1;
}
return 0;
}
{
if (avail < 0) {
if (!alsa_recover (handle)) {
}
}
if (avail < 0) {
"Could not obtain number of available frames\n");
return -1;
}
}
return avail;
}
{
int samples;
if (!live) {
return 0;
}
if (avail < 0) {
dolog ("Could not get number of available playback frames\n");
return 0;
}
while (samples) {
while (len) {
if (written <= 0) {
switch (written) {
case 0:
}
goto exit;
case -EPIPE:
len);
goto exit;
}
dolog ("Recovering from playback xrun\n");
}
continue;
case -ESTRPIPE:
/* stream is suspended and waiting for an
application recovery */
#ifndef VBOX
#else
LogRel(("ALSA: Failed to resume output stream\n"));
#endif
goto exit;
}
dolog ("Resuming suspended output stream\n");
}
continue;
case -EAGAIN:
goto exit;
default:
goto exit;
}
}
}
}
exit:
return decr;
}
{
ldebug ("alsa_fini\n");
}
}
{
struct alsa_params_req req;
struct alsa_params_obt obt;
int endianness;
int err;
return -1;
}
if (err) {
return -1;
}
dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
return -1;
}
return 0;
}
static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pauseit) /* VBOX: s/pause/pauseit/; -Wshadow */
{
int err;
if (pauseit) {
if (err < 0) {
return -1;
}
}
else {
if (err < 0) {
return -1;
}
}
return 0;
}
{
switch (cmd) {
case VOICE_ENABLE:
ldebug ("enabling voice\n");
case VOICE_DISABLE:
ldebug ("disabling voice\n");
}
return -1;
}
{
struct alsa_params_req req;
struct alsa_params_obt obt;
int endianness;
int err;
return -1;
}
if (err) {
return -1;
}
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
return -1;
}
return 0;
}
{
}
}
{
int i;
int decr;
struct {
int add;
int len;
} bufs[2];
if (!dead) {
return 0;
}
if (avail < 0) {
dolog ("Could not get number of captured frames\n");
return 0;
}
if (!avail) {
switch (state) {
case SND_PCM_STATE_PREPARED:
break;
case SND_PCM_STATE_SUSPENDED:
/* stream is suspended and waiting for an application recovery */
#ifndef VBOX
dolog ("Failed to resume suspended input stream\n");
#else
LogRel(("ALSA: Failed to resume input stream\n"));
#endif
return 0;
}
dolog ("Resuming suspended input stream\n");
}
break;
default:
}
return 0;
}
}
if (!decr) {
return 0;
}
}
else {
}
for (i = 0; i < 2; ++i) {
void *src;
while (len) {
if (nread <= 0) {
switch (nread) {
case 0:
}
goto exit;
case -EPIPE:
goto exit;
}
dolog ("Recovering from capture xrun\n");
}
continue;
case -EAGAIN:
goto exit;
default:
"Failed to read %ld frames from %p\n",
len,
);
goto exit;
}
}
read_samples += nread;
}
}
exit:
return read_samples;
}
{
}
{
switch (cmd) {
case VOICE_ENABLE:
ldebug ("enabling voice\n");
case VOICE_DISABLE:
ldebug ("disabling voice\n");
}
return -1;
}
#ifdef VBOX
{
/* ignore */
}
#endif
static void *alsa_audio_init (void)
{
#ifdef VBOX
int rc;
rc = audioLoadAlsaLib();
if (RT_FAILURE(rc)) {
return NULL;
}
#endif
return &conf;
}
static void alsa_audio_fini (void *opaque)
{
(void) opaque;
}
static struct audio_option alsa_options[] = {
"(undocumented)", NULL, 0},
"DAC device name (for instance dmix)", NULL, 0},
"ADC device name", NULL, 0},
"Behave in a more verbose way", NULL, 0},
};
static struct audio_pcm_ops alsa_pcm_ops = {
};
struct audio_driver alsa_audio_driver = {
};