⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 alsaaudio.c

📁 qemu虚拟机代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * 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. */#include <alsa/asoundlib.h>#include "vl.h"#define AUDIO_CAP "alsa"#include "audio_int.h"typedef struct ALSAVoiceOut {    HWVoiceOut hw;    void *pcm_buf;    snd_pcm_t *handle;} ALSAVoiceOut;typedef struct ALSAVoiceIn {    HWVoiceIn hw;    snd_pcm_t *handle;    void *pcm_buf;} ALSAVoiceIn;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 buffer_size_in_overriden;    int period_size_in_overriden;    int buffer_size_out_overriden;    int period_size_out_overriden;    int verbose;} conf = {#ifdef HIGH_LATENCY    .size_in_usec_in = 1,    .size_in_usec_out = 1,#endif    .pcm_name_out = "hw:0,0",    .pcm_name_in = "hw:0,0",#ifdef HIGH_LATENCY    .buffer_size_in = 400000,    .period_size_in = 400000 / 4,    .buffer_size_out = 400000,    .period_size_out = 400000 / 4,#else#define DEFAULT_BUFFER_SIZE 1024#define DEFAULT_PERIOD_SIZE 256    .buffer_size_in = DEFAULT_BUFFER_SIZE * 4,    .period_size_in = DEFAULT_PERIOD_SIZE * 4,    .buffer_size_out = DEFAULT_BUFFER_SIZE,    .period_size_out = DEFAULT_PERIOD_SIZE,    .buffer_size_in_overriden = 0,    .buffer_size_out_overriden = 0,    .period_size_in_overriden = 0,    .period_size_out_overriden = 0,#endif    .threshold = 0,    .verbose = 0};struct alsa_params_req {    int freq;    audfmt_e fmt;    int nchannels;    unsigned int buffer_size;    unsigned int period_size;};struct alsa_params_obt {    int freq;    audfmt_e fmt;    int nchannels;    snd_pcm_uframes_t samples;};static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...){    va_list ap;    va_start (ap, fmt);    AUD_vlog (AUDIO_CAP, fmt, ap);    va_end (ap);    AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));}static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (    int err,    const char *typ,    const char *fmt,    ...    ){    va_list ap;    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);    va_start (ap, fmt);    AUD_vlog (AUDIO_CAP, fmt, ap);    va_end (ap);    AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));}static void alsa_anal_close (snd_pcm_t **handlep){    int err = snd_pcm_close (*handlep);    if (err) {        alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep);    }    *handlep = NULL;}static int alsa_write (SWVoiceOut *sw, void *buf, int len){    return audio_pcm_sw_write (sw, buf, len);}static int aud_to_alsafmt (audfmt_e fmt){    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;    default:        dolog ("Internal logic error: Bad audio format %d\n", fmt);#ifdef DEBUG_AUDIO        abort ();#endif        return SND_PCM_FORMAT_U8;    }}static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness){    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;    default:        dolog ("Unrecognized audio format %d\n", alsafmt);        return -1;    }    return 0;}#if defined DEBUG_MISMATCHES || defined DEBUGstatic void alsa_dump_info (struct alsa_params_req *req,                            struct alsa_params_obt *obt){    dolog ("parameter | requested value | obtained value\n");    dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt);    dolog ("channels  |      %10d |     %10d\n",           req->nchannels, obt->nchannels);    dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);    dolog ("============================================\n");    dolog ("requested: buffer size %d period size %d\n",           req->buffer_size, req->period_size);    dolog ("obtained: samples %ld\n", obt->samples);}#endifstatic void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold){    int err;    snd_pcm_sw_params_t *sw_params;    snd_pcm_sw_params_alloca (&sw_params);    err = snd_pcm_sw_params_current (handle, sw_params);    if (err < 0) {        dolog ("Could not fully initialize DAC\n");        alsa_logerr (err, "Failed to get current software parameters\n");        return;    }    err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold);    if (err < 0) {        dolog ("Could not fully initialize DAC\n");        alsa_logerr (err, "Failed to set software threshold to %ld\n",                     threshold);        return;    }    err = snd_pcm_sw_params (handle, sw_params);    if (err < 0) {        dolog ("Could not fully initialize DAC\n");        alsa_logerr (err, "Failed to set software parameters\n");        return;    }}static int alsa_open (int in, struct alsa_params_req *req,                      struct alsa_params_obt *obt, snd_pcm_t **handlep){    snd_pcm_t *handle;    snd_pcm_hw_params_t *hw_params;    int err, freq, nchannels;    const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out;    unsigned int period_size, buffer_size;    snd_pcm_uframes_t obt_buffer_size;    const char *typ = in ? "ADC" : "DAC";    freq = req->freq;    period_size = req->period_size;    buffer_size = req->buffer_size;    nchannels = req->nchannels;    snd_pcm_hw_params_alloca (&hw_params);    err = snd_pcm_open (        &handle,        pcm_name,        in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,        SND_PCM_NONBLOCK        );    if (err < 0) {        alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name);        return -1;    }    err = snd_pcm_hw_params_any (handle, hw_params);    if (err < 0) {        alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");        goto err;    }    err = snd_pcm_hw_params_set_access (        handle,        hw_params,        SND_PCM_ACCESS_RW_INTERLEAVED        );    if (err < 0) {        alsa_logerr2 (err, typ, "Failed to set access type\n");        goto err;    }    err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);    if (err < 0) {        alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);        goto err;    }    err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);    if (err < 0) {        alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq);        goto err;    }    err = snd_pcm_hw_params_set_channels_near (        handle,        hw_params,        &nchannels        );    if (err < 0) {        alsa_logerr2 (err, typ, "Failed to set number of channels %d\n",                      req->nchannels);        goto err;    }    if (nchannels != 1 && nchannels != 2) {        alsa_logerr2 (err, typ,                      "Can not handle obtained number of channels %d\n",                      nchannels);        goto err;    }    if (!((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out))) {        if (!buffer_size) {            buffer_size = DEFAULT_BUFFER_SIZE;            period_size= DEFAULT_PERIOD_SIZE;        }    }    if (buffer_size) {        if ((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out)) {            if (period_size) {                err = snd_pcm_hw_params_set_period_time_near (                    handle,                    hw_params,                    &period_size,                    0                    );                if (err < 0) {                    alsa_logerr2 (err, typ,                                  "Failed to set period time %d\n",                                  req->period_size);                    goto err;                }            }            err = snd_pcm_hw_params_set_buffer_time_near (                handle,                hw_params,                &buffer_size,                0                );            if (err < 0) {                alsa_logerr2 (err, typ,                              "Failed to set buffer time %d\n",                              req->buffer_size);                goto err;            }        }        else {            int dir;            snd_pcm_uframes_t minval;            if (period_size) {                minval = period_size;                dir = 0;                err = snd_pcm_hw_params_get_period_size_min (                    hw_params,                    &minval,                    &dir                    );                if (err < 0) {                    alsa_logerr (                        err,                        "Could not get minmal period size for %s\n",                        typ                        );                }                else {                    if (period_size < minval) {                        if ((in && conf.period_size_in_overriden)                            || (!in && conf.period_size_out_overriden)) {                            dolog ("%s period size(%d) is less "                                   "than minmal period size(%ld)\n",                                   typ,                                   period_size,                                   minval);                        }                        period_size = minval;                    }                }                err = snd_pcm_hw_params_set_period_size (                    handle,                    hw_params,                    period_size,                    0                    );                if (err < 0) {                    alsa_logerr2 (err, typ, "Failed to set period size %d\n",                                  req->period_size);                    goto err;                }            }            minval = buffer_size;            err = snd_pcm_hw_params_get_buffer_size_min (                hw_params,                &minval                );            if (err < 0) {                alsa_logerr (err, "Could not get minmal buffer size for %s\n",                             typ);            }            else {                if (buffer_size < minval) {                    if ((in && conf.buffer_size_in_overriden)                        || (!in && conf.buffer_size_out_overriden)) {                        dolog (                            "%s buffer size(%d) is less "                            "than minimal buffer size(%ld)\n",                            typ,                            buffer_size,                            minval                            );                    }                    buffer_size = minval;                }            }            err = snd_pcm_hw_params_set_buffer_size (                handle,                hw_params,                buffer_size                );            if (err < 0) {                alsa_logerr2 (err, typ, "Failed to set buffer size %d\n",                              req->buffer_size);                goto err;            }        }    }    else {        dolog ("warning: Buffer size is not set\n");    }    err = snd_pcm_hw_params (handle, hw_params);    if (err < 0) {        alsa_logerr2 (err, typ, "Failed to apply audio parameters\n");        goto err;    }    err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size);    if (err < 0) {        alsa_logerr2 (err, typ, "Failed to get buffer size\n");        goto err;    }    err = snd_pcm_prepare (handle);    if (err < 0) {        alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);        goto err;    }    if (!in && conf.threshold) {        snd_pcm_uframes_t threshold;        int bytes_per_sec;        bytes_per_sec = freq            << (nchannels == 2)            << (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16);        threshold = (conf.threshold * bytes_per_sec) / 1000;        alsa_set_threshold (handle, threshold);    }    obt->fmt = req->fmt;    obt->nchannels = nchannels;    obt->freq = freq;    obt->samples = obt_buffer_size;    *handlep = handle;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -