`
mmdev
  • 浏览: 12847117 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

Android Audio代码分析11 - AudioStreamOutALSA::write函数

 
阅读更多
在看音频数据是怎么写的时候,在MixerThread的threadloop函数中,有以下代码完成了往硬件写数据:
int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);


mOutput来历:
函数AudioFlinger::openOutput中创建了一个MixerThread对象,并将前面调用mAudioHardware->openOutputStream得到的output作为参数传入。
MixerThread继承自PlaybackThread,在PlaybackThread的构造函数中将传入的output赋值给了mOutput。


函数AudioFlinger::openOutput以前已经打过交道。mAudioHardware其实是一个AudioHardwareALSA对象。
调用mAudioHardware->openOutputStream得到的其实是一个AudioStreamOutALSA对象。


所以,mOutput->write,其实就是函数AudioStreamOutALSA::write。


*****************************************源码*************************************************
ssize_t AudioStreamOutALSA::write(const void *buffer, size_t bytes)
{
    AutoMutex lock(mLock);


    if (!mPowerLock) {
        acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock");
        mPowerLock = true;
    }


    acoustic_device_t *aDev = acoustics();


    // For output, we will pass the data on to the acoustics module, but the actual
    // data is expected to be sent to the audio device directly as well.
    if (aDev && aDev->write)
        aDev->write(aDev, buffer, bytes);


    snd_pcm_sframes_t n;
    size_t            sent = 0;
    status_t          err;


    do {
        if (mHandle->mmap)
            n = snd_pcm_mmap_writei(mHandle->handle,
                               (char *)buffer + sent,
                               snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent));
else
            n = snd_pcm_writei(mHandle->handle,
                               (char *)buffer + sent,
                               snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent));
        if (n == -EBADFD) {
            // Somehow the stream is in a bad state. The driver probably
            // has a bug and snd_pcm_recover() doesn't seem to handle this.
            mHandle->module->open(mHandle, mHandle->curDev, mHandle->curMode);


            if (aDev && aDev->recover) aDev->recover(aDev, n);
        }
        else if (n < 0) {
            if (mHandle->handle) {
LOGW("underrun and do recovery.....");
                // snd_pcm_recover() will return 0 if successful in recovering from
                // an error, or -errno if the error was unrecoverable.
                n = snd_pcm_recover(mHandle->handle, n, 1);


                if (aDev && aDev->recover) aDev->recover(aDev, n);


                if (n) return static_cast<ssize_t>(n);
            }
        }
        else {
            mFrameCount += n;
            sent += static_cast<ssize_t>(snd_pcm_frames_to_bytes(mHandle->handle, n));
        }


    } while (mHandle->handle && sent < bytes);


    return sent;
}


**********************************************************************************************
源码路径:
hardware\alsa_sound\AudioStreamOutALSA.cpp


#######################说明################################

ssize_t AudioStreamOutALSA::write(const void *buffer, size_t bytes)
{
    AutoMutex lock(mLock);


    if (!mPowerLock) {
        acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock");
        mPowerLock = true;
    }


// 看看acoustics是个什么东东
    acoustic_device_t *aDev = acoustics();
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
acoustic_device_t *ALSAStreamOps::acoustics()
{
    return mParent->mAcousticDevice;
}
ALSAStreamOps是AudioStreamOutALSA的父类。
mParent是在ALSAStreamOps的构造函数中赋值的。
AudioStreamOutALSA对象是在AudioHardwareALSA::openOutputStream中创建。
out = new AudioStreamOutALSA(this, &(*it));
在ALSAStreamOps的构造函数中,将传入的this赋值给了mParent。
mAcousticDevice其实是AudioHardwareALSA的成员变量。其赋值在AudioHardwareALSA的构造函数中完成:
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    err = hw_get_module(ACOUSTICS_HARDWARE_MODULE_ID,
            (hw_module_t const**)&module);


    if (err == 0) {
        hw_device_t* device;
        err = module->methods->open(module, ACOUSTICS_HARDWARE_NAME, &device);
        if (err == 0)
            mAcousticDevice = (acoustic_device_t *)device;
        else
            LOGE("Acoustics Module not found.");
    }
// ----------------------------------------------------------------
// ----------------------------------------------------------------


    // For output, we will pass the data on to the acoustics module, but the actual
    // data is expected to be sent to the audio device directly as well.
    if (aDev && aDev->write)
        aDev->write(aDev, buffer, bytes);


    snd_pcm_sframes_t n;
    size_t            sent = 0;
    status_t          err;


    do {
// 写数据是在这儿完成的。
// 有两个关键的东东需要确认:
// 1、mHandle是怎么来的。
// 2、函数snd_pcm_mmap_writei是怎么实现的。
        if (mHandle->mmap)
            n = snd_pcm_mmap_writei(mHandle->handle,
                               (char *)buffer + sent,
                               snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent));
else
            n = snd_pcm_writei(mHandle->handle,
                               (char *)buffer + sent,
                               snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent));
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/* 先看看第一个问题:
1、mHandle是怎么来的。
mHandle也是在ALSAStreamOps的构造函数中赋值的。
所赋值其实就是以下代码中的it。
out = new AudioStreamOutALSA(this, &(*it));

*/
// it的来历:
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    // Find the appropriate alsa device
// mALSADevice的产生,在看函数AudioHardwareALSA::openOutputStream的时候已经知道
    for(ALSAHandleList::iterator it = mDeviceList.begin();
        it != mDeviceList.end(); ++it)
        if (it->devices & devices) {
// open函数以前也有见过,不过当时没有彻底啃干净
// 在看函数AudioHardwareALSA::openOutputStream的时候,了解到,此处其实是调用的函数s_open
            err = mALSADevice->open(&(*it), devices, mode());
// +++++++++++++++++++++++++++++s_open+++++++++++++++++++++++++++++++++++
static status_t s_open(alsa_handle_t *handle, uint32_t devices, int mode)
{
    // Close off previously opened device.
    // It would be nice to determine if the underlying device actually
    // changes, but we might be recovering from an error or manipulating
    // mixer settings (see asound.conf).
    //
    s_close(handle);
// +++++++++++++++++++++++++++++s_close+++++++++++++++++++++++++++++++++++
static status_t s_close(alsa_handle_t *handle)
{
    LOGW("s_close--");
    status_t err = NO_ERROR;
    snd_pcm_t *h = handle->handle;
    handle->handle = 0;
    handle->curDev = 0;
    handle->curMode = 0;
    if (h) {
        snd_pcm_drain(h);
// +++++++++++++++++++++++++++++snd_pcm_drain+++++++++++++++++++++++++++++++++++
/**
 * \brief Stop a PCM preserving pending frames
 * \param pcm PCM handle
 * \return 0 on success otherwise a negative error code
 * \retval -ESTRPIPE a suspend event occurred
 *
 * For playback wait for all pending frames to be played and then stop
 * the PCM.
 * For capture stop PCM permitting to retrieve residual frames.
 *
 * For stopping the PCM stream immediately, use \link ::snd_pcm_drop() \endlink
 * instead.
 */
int snd_pcm_drain(snd_pcm_t *pcm)
{
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
// 调到了snd_pcm_t中的东东
// 又回到了原来的话题,alsa_handle_t结构体的来历
return pcm->fast_ops->drain(pcm->fast_op_arg);
}
// -----------------------------snd_pcm_drain-----------------------------------
        err = snd_pcm_close(h);
// ++++++++++++++++++++++++++++snd_pcm_close++++++++++++++++++++++++++++++++++++
/**
 * \brief close PCM handle
 * \param pcm PCM handle
 * \return 0 on success otherwise a negative error code
 *
 * Closes the specified PCM handle and frees all associated
 * resources.
 */
int snd_pcm_close(snd_pcm_t *pcm)
{
int res = 0, err;
assert(pcm);
if (pcm->setup && !pcm->donot_close) {
snd_pcm_drop(pcm);
// +++++++++++++++++++++++++++snd_pcm_drop+++++++++++++++++++++++++++++++++++++
/**
 * \brief Stop a PCM dropping pending frames
 * \param pcm PCM handle
 * \return 0 on success otherwise a negative error code
 *
 * This function stops the PCM <i>immediately</i>.
 * The pending samples on the buffer are ignored.
 *
 * For processing all pending samples, use \link ::snd_pcm_drain() \endlink
 * instead.
 */
int snd_pcm_drop(snd_pcm_t *pcm)
{
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
return pcm->fast_ops->drop(pcm->fast_op_arg);
}
// ---------------------------snd_pcm_drop-------------------------------------
err = snd_pcm_hw_free(pcm);
// ++++++++++++++++++++++++++++++snd_pcm_hw_free++++++++++++++++++++++++++++++++++
/** \brief Remove PCM hardware configuration and free associated resources
 * \param pcm PCM handle
 * \return 0 on success otherwise a negative error code
 */
int snd_pcm_hw_free(snd_pcm_t *pcm)
{
int err;
if (! pcm->setup)
return 0;
if (pcm->mmap_channels) {
err = snd_pcm_munmap(pcm);
// +++++++++++++++++++++++++snd_pcm_munmap+++++++++++++++++++++++++++++++++++++++
int snd_pcm_munmap(snd_pcm_t *pcm)
{
int err;
unsigned int c;
assert(pcm);
if (CHECK_SANITY(! pcm->mmap_channels)) {
SNDMSG("Not mmapped");
return -ENXIO;
}
if (pcm->mmap_shadow)
return pcm->ops->munmap(pcm);
for (c = 0; c < pcm->channels; ++c) {
snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
unsigned int c1;
size_t size = i->first + i->step * (pcm->buffer_size - 1) + pcm->sample_bits;
if (!i->addr)
continue;
for (c1 = c + 1; c1 < pcm->channels; ++c1) {
snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
size_t s;
if (i1->addr != i->addr)
continue;
i1->addr = NULL;
s = i1->first + i1->step * (pcm->buffer_size - 1) + pcm->sample_bits;
if (s > size)
size = s;
}
size = (size + 7) / 8;
size = page_align(size);
switch (i->type) {
case SND_PCM_AREA_MMAP:
err = munmap(i->addr, size);
if (err < 0) {
SYSERR("mmap failed");
return -errno;
}
errno = 0;
break;
#ifndef ANDROID
case SND_PCM_AREA_SHM:
if (i->u.shm.area) {
snd_shm_area_destroy(i->u.shm.area);
// +++++++++++++++++++++++snd_shm_area_destroy+++++++++++++++++++++++++++++++++++++++++
/**
 * \brief Release the shared area record
 * \param area the shared are record
 * \return 0 if successful, or a negative error code
 *
 * Decreases the reference counter of the given shared area record, and
 * releases the resources automaticall if it reaches to 0.
 */
int snd_shm_area_destroy(struct snd_shm_area *area)
{
if (area == NULL)
return -ENOENT;
if (--area->share)
return 0;
list_del(&area->list);
shmdt(area->ptr);
free(area);
return 0;
}
// -----------------------snd_shm_area_destroy-----------------------------------------
i->u.shm.area = NULL;
if (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
   pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
unsigned int c1;
for (c1 = c + 1; c1 < pcm->channels; c1++) {
snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
if (i1->u.shm.area) {
snd_shm_area_destroy(i1->u.shm.area);
i1->u.shm.area = NULL;
}
}
}
}
break;
#endif
case SND_PCM_AREA_LOCAL:
free(i->addr);
break;
default:
assert(0);
}
i->addr = NULL;
}
err = pcm->ops->munmap(pcm);
if (err < 0)
return err;
free(pcm->mmap_channels);
free(pcm->running_areas);
pcm->mmap_channels = NULL;
pcm->running_areas = NULL;
return 0;
}
// -------------------------snd_pcm_munmap---------------------------------------
if (err < 0)
return err;
}
// assert(snd_pcm_state(pcm) == SND_PCM_STATE_SETUP ||
//        snd_pcm_state(pcm) == SND_PCM_STATE_PREPARED);
err = pcm->ops->hw_free(pcm->op_arg);
pcm->setup = 0;
if (err < 0)
return err;
return 0;
}
// ------------------------------snd_pcm_hw_free----------------------------------
if (err < 0)
res = err;
}
if (pcm->mmap_channels)
snd_pcm_munmap(pcm);
while (!list_empty(&pcm->async_handlers)) {
snd_async_handler_t *h = list_entry(pcm->async_handlers.next, snd_async_handler_t, hlist);
snd_async_del_handler(h);
}
err = pcm->ops->close(pcm->op_arg);
if (err < 0)
res = err;
err = snd_pcm_free(pcm);
if (err < 0)
res = err;
return res;
} 
// ----------------------------snd_pcm_close------------------------------------
    }


    return err;
}
// 做了些清除工作。
// -----------------------------s_close-----------------------------------


    LOGD("open called for devices %08x in mode %d...", devices, mode);


    const char *stream = streamName(handle);
// +++++++++++++++++++++++++++++streamName+++++++++++++++++++++++++++++++++++
const char *streamName(alsa_handle_t *handle)
{
    return snd_pcm_stream_name(direction(handle));
// +++++++++++++++++++++++++snd_pcm_stream_name+++++++++++++++++++++++++++++++++++++++
/**
 * \brief get name of PCM stream type
 * \param stream PCM stream type
 * \return ascii name of PCM stream type
 */
const char *snd_pcm_stream_name(snd_pcm_stream_t stream)
{
if (stream > SND_PCM_STREAM_LAST)
return NULL;
return snd_pcm_stream_names[stream];
// +++++++++++++++++++++++++++snd_pcm_stream_names+++++++++++++++++++++++++++++++++++++
static const char *const snd_pcm_stream_names[] = {
// #define STREAM(v) [SND_PCM_STREAM_##v] = #v
STREAM(PLAYBACK),
STREAM(CAPTURE),
};
  // ---------------------------snd_pcm_stream_names-------------------------------------
}
// -------------------------snd_pcm_stream_name---------------------------------------
}
// -----------------------------streamName-----------------------------------
    const char *devName = deviceName(handle, devices, mode, 1);
// ++++++++++++++++++++++++++deviceName++++++++++++++++++++++++++++++++++++++
//card_device =0, return the card name, card_device=1, return the card device name
const char *deviceName(alsa_handle_t *alsa_handle, uint32_t device, int mode, int card_device)
{


snd_ctl_t *handle;
int card, err, dev, idx;
snd_ctl_card_info_t *info;
snd_pcm_info_t *pcminfo;
snd_ctl_card_info_alloca(&info);
snd_pcm_info_alloca(&pcminfo);
    int  cardnum = 0;
    char value[PROPERTY_VALUE_MAX];
    snd_pcm_stream_t stream = direction(alsa_handle);
// +++++++++++++++++++++++++++direction+++++++++++++++++++++++++++++++++++++
snd_pcm_stream_t direction(alsa_handle_t *handle)
{
    return (handle->devices & AudioSystem::DEVICE_OUT_ALL) ? SND_PCM_STREAM_PLAYBACK
            : SND_PCM_STREAM_CAPTURE;
}
// ---------------------------direction-------------------------------------
    bool havespdifdevice = false;
    bool havesgtldevice = false;
    
card = -1;
if (snd_card_next(&card) < 0 || card < 0) {
LOGD("no soundcards found...");
return "default";
}
// ++++++++++++++++++++++++++++snd_card_next++++++++++++++++++++++++++++++++++++
external\alsa-lib\src\control\Cards.c
/**
 * \brief Try to determine the next card.
 * \param rcard pointer to card number
 * \result zero if success, otherwise a negative error code
 *
 * Tries to determine the next card from given card number.
 * If card number is -1, then the first available card is
 * returned. If the result card number is -1, no more cards
 * are available.
 */
int snd_card_next(int *rcard)
{
int card;

if (rcard == NULL)
return -EINVAL;
card = *rcard;
card = card < 0 ? 0 : card + 1;
for (; card < 32; card++) {
if (snd_card_load(card)) {
// +++++++++++++++++++++++++++snd_card_load+++++++++++++++++++++++++++++++++++++
/**
 * \brief Try to load the driver for a card.
 * \param card Card number.
 * \return 1 if driver is present, zero if driver is not present
 */
int snd_card_load(int card)
{
return !!(snd_card_load1(card) >= 0);
// +++++++++++++++++++++++++++++snd_card_load1+++++++++++++++++++++++++++++++++++
static int snd_card_load1(int card)
{
int res;
char control[sizeof(SND_FILE_CONTROL) + 10];


sprintf(control, SND_FILE_CONTROL, card);
res = snd_card_load2(control);
// ++++++++++++++++++++++++++++snd_card_load2++++++++++++++++++++++++++++++++++++
static int snd_card_load2(const char *control)
{
int open_dev;
snd_ctl_card_info_t info;


open_dev = snd_open_device(control, O_RDONLY);
// +++++++++++++++++++++++++snd_open_device+++++++++++++++++++++++++++++++++++++++
static inline int snd_open_device(const char *filename, int fmode)
{
int fd;


#ifdef O_CLOEXEC
fmode |= O_CLOEXEC;
#endif
fd = open(filename, fmode);


/* open with resmgr */
#ifdef SUPPORT_RESMGR
if (fd < 0) {
if (errno == EAGAIN || errno == EBUSY)
return fd;
if (! access(filename, F_OK))
fd = rsm_open_device(filename, fmode);
}
#endif
if (fd >= 0)
fcntl(fd, F_SETFD, FD_CLOEXEC);
return fd;
}
// -------------------------snd_open_device---------------------------------------
if (open_dev >= 0) {
if (ioctl(open_dev, SNDRV_CTL_IOCTL_CARD_INFO, &info) < 0) {
int err = -errno;
close(open_dev);
return err;
}
close(open_dev);
return info.card;
} else {
return -errno;
}
}
// ----------------------------snd_card_load2------------------------------------
#ifdef SUPPORT_ALOAD
if (res < 0) {
char aload[sizeof(SND_FILE_LOAD) + 10];
sprintf(aload, SND_FILE_LOAD, card);
res = snd_card_load2(aload);
}
#endif
return res;
}
// -----------------------------snd_card_load1-----------------------------------
}
// ---------------------------snd_card_load-------------------------------------
*rcard = card;
return 0;
}
}
*rcard = -1;
return 0;
}
// ----------------------------snd_card_next------------------------------------
LOGD("**** List of %s Hardware Devices ****\n",
      snd_pcm_stream_name(stream));
while (card >= 0) {
char name[32];
sprintf(name, "hw:%d", card);
if ((err = snd_ctl_open(&handle, name, 0)) < 0) {
LOGD("control open (%i): %s", card, snd_strerror(err));
goto next_card;
}
// +++++++++++++++++++++++++++snd_ctl_open+++++++++++++++++++++++++++++++++++++
/**
 * \brief Opens a CTL
 * \param ctlp Returned CTL handle
 * \param name ASCII identifier of the CTL handle
 * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC)
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode)
{
int err;
assert(ctlp && name);
err = snd_config_update();
// +++++++++++++++++++++++++++snd_config_update+++++++++++++++++++++++++++++++++++++
/** 
 * \brief Updates #snd_config by rereading the global configuration files (if needed).
 * \return 0 if #snd_config was up to date, 1 if #snd_config was
 *         updated, otherwise a negative error code.
 *
 * \warning Whenever #snd_config is updated, all string pointers and
 * configuration node handles previously obtained from it may become
 * invalid.
 *
 * \par Errors:
 * Any errors encountered when parsing the input or returned by hooks or
 * functions.
 *
 * \par Conforming to:
 * LSB 3.2
 */
int snd_config_update(void)
{
int err;


#ifdef HAVE_LIBPTHREAD
pthread_mutex_lock(&snd_config_update_mutex);
#endif
err = snd_config_update_r(&snd_config, &snd_config_global_update, NULL);
// ++++++++++++++++++++++snd_config_update_r++++++++++++++++++++++++++++++++++++++++++
/** 
 * \brief Updates a configuration tree by rereading the configuration files (if needed).
 * \param[in,out] _top Address of the handle to the top-level node.
 * \param[in,out] _update Address of a pointer to private update information.
 * \param[in] cfgs A list of configuration file names, delimited with ':'.
 *                 If \p cfgs is \c NULL, the default global
 *                 configuration file is used.
 * \return 0 if \a _top was up to date, 1 if the configuration files
 *         have been reread, otherwise a negative error code.
 *
 * The variables pointed to by \a _top and \a _update can be initialized
 * to \c NULL before the first call to this function.  The private
 * update information holds information about all used configuration
 * files that allows this function to detects changes to them; this data
 * can be freed with #snd_config_update_free.
 *
 * The global configuration files are specified in the environment variable
 * \c ALSA_CONFIG_PATH.
 *
 * \warning If the configuration tree is reread, all string pointers and
 * configuration node handles previously obtained from this tree become
 * invalid.
 *
 * \par Errors:
 * Any errors encountered when parsing the input or returned by hooks or
 * functions.
 */
int snd_config_update_r(snd_config_t **_top, snd_config_update_t **_update, const char *cfgs)
{
int err;
const char *configs, *c;
unsigned int k;
size_t l;
snd_config_update_t *local;
snd_config_update_t *update;
snd_config_t *top;

assert(_top && _update);
top = *_top;
update = *_update;
configs = cfgs;
if (!configs) {
/** The name of the environment variable containing the files list for #snd_config_update. */
// #define ALSA_CONFIG_PATH_VAR "ALSA_CONFIG_PATH"
configs = getenv(ALSA_CONFIG_PATH_VAR);
if (!configs || !*configs)
/** The name of the default files used by #snd_config_update. */
// #define ALSA_CONFIG_PATH_DEFAULT ALSA_CONFIG_DIR "/alsa.conf"
configs = ALSA_CONFIG_PATH_DEFAULT;
}
for (k = 0, c = configs; (l = strcspn(c, ": ")) > 0; ) {
c += l;
k++;
if (!*c)
break;
c++;
}
if (k == 0) {
local = NULL;
goto _reread;
}
local = (snd_config_update_t *)calloc(1, sizeof(snd_config_update_t));
if (!local)
return -ENOMEM;
local->count = k;
local->finfo = calloc(local->count, sizeof(struct finfo));
if (!local->finfo) {
free(local);
return -ENOMEM;
}
for (k = 0, c = configs; (l = strcspn(c, ": ")) > 0; ) {
char name[l + 1];
memcpy(name, c, l);
name[l] = 0;
err = snd_user_file(name, &local->finfo[k].name);
if (err < 0)
goto _end;
c += l;
k++;
if (!*c)
break;
c++;
}
for (k = 0; k < local->count; ++k) {
struct stat st;
struct finfo *lf = &local->finfo[k];
if (stat(lf->name, &st) >= 0) {
lf->dev = st.st_dev;
lf->ino = st.st_ino;
lf->mtime = st.st_mtime;
} else {
SNDERR("Cannot access file %s", lf->name);
free(lf->name);
memmove(&local->finfo[k], &local->finfo[k+1], sizeof(struct finfo) * (local->count - k - 1));
k--;
local->count--;
}
}
if (!update)
goto _reread;
if (local->count != update->count)
goto _reread;
for (k = 0; k < local->count; ++k) {
struct finfo *lf = &local->finfo[k];
struct finfo *uf = &update->finfo[k];
if (strcmp(lf->name, uf->name) != 0 ||
   lf->dev != uf->dev ||
   lf->ino != uf->ino ||
   lf->mtime != uf->mtime)
goto _reread;
}
err = 0;


 _end:
if (err < 0) {
if (top) {
snd_config_delete(top);
*_top = NULL;
}
if (update) {
snd_config_update_free(update);
*_update = NULL;
}
}
if (local)
snd_config_update_free(local);
return err;


 _reread:
  *_top = NULL;
  *_update = NULL;
  if (update) {
  snd_config_update_free(update);
  update = NULL;
  }
if (top) {
snd_config_delete(top);
top = NULL;
}
err = snd_config_top(&top);
// +++++++++++++++++++++++++++snd_config_top+++++++++++++++++++++++++++++++++++++
/**
 * \brief Creates a top level configuration node.
 * \param[out] config Handle to the new node.
 * \return Zero if successful, otherwise a negative error code.
 *
 * The returned node is an empty compound node without a parent and
 * without an id.
 *
 * \par Errors:
 * <dl>
 * <dt>-ENOMEM<dd>Out of memory.
 * </dl>
 *
 * \par Conforming to:
 * LSB 3.2
 */
int snd_config_top(snd_config_t **config)
{
assert(config);
return _snd_config_make(config, 0, SND_CONFIG_TYPE_COMPOUND);
// +++++++++++++++++++++++++_snd_config_make+++++++++++++++++++++++++++++++++++++++
static int _snd_config_make(snd_config_t **config, char **id, snd_config_type_t type)
{
snd_config_t *n;
assert(config);
n = calloc(1, sizeof(*n));
if (n == NULL) {
if (*id) {
free(*id);
*id = NULL;
}
return -ENOMEM;
}
if (id) {
n->id = *id;
*id = NULL;
}
n->type = type;
if (type == SND_CONFIG_TYPE_COMPOUND)
INIT_LIST_HEAD(&n->u.compound.fields);
// ++++++++++++++++++++++++INIT_LIST_HEAD++++++++++++++++++++++++++++++++++++++++
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
// ------------------------INIT_LIST_HEAD----------------------------------------
*config = n;
return 0;
}
// -------------------------_snd_config_make---------------------------------------
}
// ---------------------------snd_config_top-------------------------------------
if (err < 0)
goto _end;
if (!local)
goto _skip;
for (k = 0; k < local->count; ++k) {
snd_input_t *in;
err = snd_input_stdio_open(&in, local->finfo[k].name, "r");
// ++++++++++++++++++++++++snd_input_stdio_open++++++++++++++++++++++++++++++++++++++++
/**
 * \brief Creates a new input object reading from a file.
 * \param inputp The functions puts the pointer to the new input object
 *               at the address specified by \p inputp.
 * \param file The name of the file to read from.
 * \param mode The open mode, like \c fopen(3).
 * \return Zero if successful, otherwise a negative error code.
 */
int snd_input_stdio_open(snd_input_t **inputp, const char *file, const char *mode)
{
int err;
FILE *fp = fopen(file, mode);
if (!fp) {
//SYSERR("fopen");
return -errno;
}
err = snd_input_stdio_attach(inputp, fp, 1);
// +++++++++++++++++++++++++snd_input_stdio_attach+++++++++++++++++++++++++++++++++++++++
/**
 * \brief Creates a new input object using an existing stdio \c FILE pointer.
 * \param inputp The function puts the pointer to the new input object
 *               at the address specified by \p inputp.
 * \param fp The \c FILE pointer to read from.
 *           Reading begins at the current file position.
 * \param _close Close flag. Set this to 1 if #snd_input_close should close
 *              \p fp by calling \c fclose.
 * \return Zero if successful, otherwise a negative error code.
 */
int snd_input_stdio_attach(snd_input_t **inputp, FILE *fp, int _close)
{
snd_input_t *input;
snd_input_stdio_t *stdio;
assert(inputp && fp);
stdio = calloc(1, sizeof(*stdio));
if (!stdio)
return -ENOMEM;
input = calloc(1, sizeof(*input));
if (!input) {
free(stdio);
return -ENOMEM;
}
stdio->fp = fp;
stdio->close = _close;
input->type = SND_INPUT_STDIO;
input->ops = &snd_input_stdio_ops;
input->private_data = stdio;
*inputp = input;
return 0;
}
// -------------------------snd_input_stdio_attach---------------------------------------
if (err < 0)
fclose(fp);
return err;
}
// ------------------------snd_input_stdio_open----------------------------------------
if (err >= 0) {
err = snd_config_load(top, in);
// +++++++++++++++++++++++snd_config_load+++++++++++++++++++++++++++++++++++++++++
/**
 * \brief Loads a configuration tree.
 * \param config Handle to a top level configuration node.
 * \param in Input handle to read the configuration from.
 * \return Zero if successful, otherwise a negative error code.
 *
 * The definitions loaded from the input are added to \a config, which
 * must be a compound node.
 *
 * \par Errors:
 * Any errors encountered when parsing the input or returned by hooks or
 * functions.
 *
 * \par Conforming to:
 * LSB 3.2
 */
int snd_config_load(snd_config_t *config, snd_input_t *in)
{
return snd_config_load1(config, in, 0);
// +++++++++++++++++++++++++++snd_config_load1+++++++++++++++++++++++++++++++++++++
static int snd_config_load1(snd_config_t *config, snd_input_t *in, int override)
{
int err;
input_t input;
struct filedesc *fd, *fd_next;
assert(config && in);
fd = malloc(sizeof(*fd));
if (!fd)
return -ENOMEM;
fd->name = NULL;
fd->in = in;
fd->line = 1;
fd->column = 0;
fd->next = NULL;
input.current = fd;
input.unget = 0;
err = parse_defs(config, &input, 0, override);
fd = input.current;
if (err < 0) {
const char *str;
switch (err) {
case LOCAL_UNTERMINATED_STRING:
str = "Unterminated string";
err = -EINVAL;
break;
case LOCAL_UNTERMINATED_QUOTE:
str = "Unterminated quote";
err = -EINVAL;
break;
case LOCAL_UNEXPECTED_CHAR:
str = "Unexpected char";
err = -EINVAL;
break;
case LOCAL_UNEXPECTED_EOF:
str = "Unexpected end of file";
err = -EINVAL;
break;
default:
str = strerror(-err);
break;
}
SNDERR("%s:%d:%d:%s", fd->name ? fd->name : "_toplevel_", fd->line, fd->column, str);
goto _end;
}
if (get_char(&input) != LOCAL_UNEXPECTED_EOF) {
SNDERR("%s:%d:%d:Unexpected }", fd->name ? fd->name : "", fd->line, fd->column);
err = -EINVAL;
goto _end;
}
 _end:
while (fd->next) {
fd_next = fd->next;
snd_input_close(fd->in);
free(fd->name);
free(fd);
fd = fd_next;
}
free(fd);
return err;
}
// ---------------------------snd_config_load1-------------------------------------
}
// -----------------------snd_config_load-----------------------------------------
snd_input_close(in);
if (err < 0) {
SNDERR("%s may be old or corrupted: consider to remove or fix it", local->finfo[k].name);
goto _end;
}
} else {
SNDERR("cannot access file %s", local->finfo[k].name);
}
}
 _skip:
err = snd_config_hooks(top, NULL);
if (err < 0) {
SNDERR("hooks failed, removing configuration");
goto _end;
}
*_top = top;
*_update = local;
return 1;
}
// ----------------------snd_config_update_r------------------------------------------
#ifdef HAVE_LIBPTHREAD
pthread_mutex_unlock(&snd_config_update_mutex);
#endif
return err;
}
// ---------------------------snd_config_update-------------------------------------
if (err < 0)
return err;
return snd_ctl_open_noupdate(ctlp, snd_config, name, mode);
// ++++++++++++++++++++++++++++snd_ctl_open_noupdate++++++++++++++++++++++++++++++++++++
static int snd_ctl_open_noupdate(snd_ctl_t **ctlp, snd_config_t *root, const char *name, int mode)
{
int err;
snd_config_t *ctl_conf;
// 这儿,只看看snd_config_search_definition的注释吧
err = snd_config_search_definition(root, "ctl", name, &ctl_conf);
// +++++++++++++++++++++++snd_config_search_definition+++++++++++++++++++++++++++++++++++++++++
/**
 * \brief Searches for a definition in a configuration tree, using
 *        aliases and expanding hooks and arguments.
 * \param[in] config Handle to the configuration (sub)tree to search.
 * \param[in] base Implicit key base, or \c NULL for none.
 * \param[in] name Key suffix, optionally with arguments.
 * \param[out] result The function puts the handle to the expanded found
 *                    node at the address specified by \a result.
 * \return A non-negative value if successful, otherwise a negative error code.
 *
 * This functions searches for a child node of \a config, allowing
 * aliases and expanding hooks, like #snd_config_search_alias_hooks.
 *
 * If \a name contains a colon (:), the rest of the string after the
 * colon contains arguments that are expanded as with
 * #snd_config_expand.
 *
 * In any case, \a result is a new node that must be freed by the
 * caller.
 *
 * \par Errors:
 * <dl>
 * <dt>-ENOENT<dd>An id in \a key or an alias id does not exist.
 * <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
 *                not a compound node.
 * </dl>
 * Additionally, any errors encountered when parsing the hook
 * definitions or arguments, or returned by (hook) functions.
 */
// -----------------------snd_config_search_definition-----------------------------------------
if (err < 0) {
SNDERR("Invalid CTL %s", name);
return err;
}
err = snd_ctl_open_conf(ctlp, name, root, ctl_conf, mode);
// ++++++++++++++++++++++snd_ctl_open_conf++++++++++++++++++++++++++++++++++++++++++
static int snd_ctl_open_conf(snd_ctl_t **ctlp, const char *name,
    snd_config_t *ctl_root, snd_config_t *ctl_conf, int mode)
{
const char *str;
char *buf = NULL, *buf1 = NULL;
int err;
snd_config_t *conf, *type_conf = NULL;
snd_config_iterator_t i, next;
const char *lib = NULL, *open_name = NULL;
const char *id;
int (*open_func)(snd_ctl_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
#ifndef PIC
extern void *snd_control_open_symbols(void);
#endif
void *h = NULL;
if (snd_config_get_type(ctl_conf) != SND_CONFIG_TYPE_COMPOUND) {
if (name)
SNDERR("Invalid type for CTL %s definition", name);
else
SNDERR("Invalid type for CTL definition");
return -EINVAL;
}
err = snd_config_search(ctl_conf, "type", &conf);
if (err < 0) {
SNDERR("type is not defined");
return err;
}
err = snd_config_get_id(conf, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(ctl_root, "ctl_type", str, &type_conf);
if (err >= 0) {
if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) {
SNDERR("Invalid type for CTL type %s definition", str);
goto _err;
}
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {
err = snd_config_get_string(n, &lib);
if (err < 0) {
SNDERR("Invalid type for %s", id);
goto _err;
}
continue;
}
if (strcmp(id, "open") == 0) {
err = snd_config_get_string(n, &open_name);
if (err < 0) {
SNDERR("Invalid type for %s", id);
goto _err;
}
continue;
}
SNDERR("Unknown field %s", id);
err = -EINVAL;
goto _err;
}
}
if (!open_name) {
buf = malloc(strlen(str) + 32);
if (buf == NULL) {
err = -ENOMEM;
goto _err;
}
open_name = buf;
sprintf(buf, "_snd_ctl_%s_open", str);
}
if (!lib) {
const char *const *build_in = build_in_ctls;
while (*build_in) {
if (!strcmp(*build_in, str))
break;
build_in++;
}
if (*build_in == NULL) {
buf1 = malloc(strlen(str) + sizeof(ALSA_PLUGIN_DIR) + 32);
if (buf1 == NULL) {
err = -ENOMEM;
goto _err;
}
lib = buf1;
sprintf(buf1, "%s/libasound_module_ctl_%s.so", ALSA_PLUGIN_DIR, str);
}
}
#ifndef PIC
snd_control_open_symbols();
#endif
open_func = snd_dlobj_cache_lookup(open_name);
if (open_func) {
err = 0;
goto _err;
}
// +++++++++++++++++++++++snd_dlobj_cache_lookup+++++++++++++++++++++++++++++++++++++++++
void *snd_dlobj_cache_lookup(const char *name)
{
struct list_head *p;
struct dlobj_cache *c;


list_for_each(p, &pcm_dlobj_list) {
c = list_entry(p, struct dlobj_cache, list);
if (strcmp(c->name, name) == 0)
return c->func;
}
return NULL;
}


// 往pcm_dlobj_list中添加对象的地方:
// ++++++++++++++++++++++++++++++snd_dlobj_cache_add++++++++++++++++++++++++++++++++++
int snd_dlobj_cache_add(const char *name, void *dlobj, void *open_func)
{
struct list_head *p;
struct dlobj_cache *c;


list_for_each(p, &pcm_dlobj_list) {
c = list_entry(p, struct dlobj_cache, list);
if (strcmp(c->name, name) == 0)
return 0; /* already exists */
}
c = malloc(sizeof(*c));
if (! c)
return -ENOMEM;
c->name = strdup(name);
if (! c->name) {
free(c);
return -ENOMEM;
}
c->obj = dlobj;
c->func = open_func;
list_add_tail(&c->list, &pcm_dlobj_list);
return 0;
}
// ------------------------------snd_dlobj_cache_add----------------------------------


// 函数snd_ctl_open_conf中调用了函数snd_dlobj_cache_add。
// -----------------------snd_dlobj_cache_lookup-----------------------------------------
h = snd_dlopen(lib, RTLD_NOW);
if (h)
open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_CONTROL_DLSYM_VERSION));
// +++++++++++++++++++++snd_dlsym+++++++++++++++++++++++++++++++++++++++++++
// 函数snd_dlsym的注释:
/**
 * \brief Resolves a symbol from a dynamic library - ALSA wrapper for \c dlsym.
 * \param handle Library handle, similar to \c dlsym.
 * \param name Symbol name.
 * \param version Version of the symbol.
 *
 * This function can emulate dynamic linking for the static build of
 * the alsa-lib library.
 *
 * This special version of the \c dlsym function checks also the version
 * of the symbol. A versioned symbol should be defined using the
 * #SND_DLSYM_BUILD_VERSION macro.
 */
// ---------------------snd_dlsym-------------------------------------------
err = 0;
if (!h) {
SNDERR("Cannot open shared library %s", lib);
err = -ENOENT;
} else if (!open_func) {
SNDERR("symbol %s is not defined inside %s", open_name, lib);
snd_dlclose(h);
err = -ENXIO;
}
       _err:
if (type_conf)
snd_config_delete(type_conf);
if (err >= 0) {
err = open_func(ctlp, name, ctl_root, ctl_conf, mode);
if (err >= 0) {
if (h /*&& (mode & SND_CTL_KEEP_ALIVE)*/) {
snd_dlobj_cache_add(open_name, h, open_func);
h = NULL;
}
(*ctlp)->dl_handle = h;
err = 0;
} else {
if (h)
snd_dlclose(h);
}
}
free(buf);
free(buf1);
return err;
}
// ----------------------snd_ctl_open_conf------------------------------------------
snd_config_delete(ctl_conf);
return err;
}
// ----------------------------snd_ctl_open_noupdate------------------------------------
}
// ---------------------------snd_ctl_open-------------------------------------
if ((err = snd_ctl_card_info(handle, info)) < 0) {
LOGD("control hardware info (%i): %s", card, snd_strerror(err));
snd_ctl_close(handle);
goto next_card;
}
// +++++++++++++++++++++++++snd_ctl_card_info+++++++++++++++++++++++++++++++++++++++
/**
 * \brief Get card related information
 * \param ctl CTL handle
 * \param info Card info pointer
 * \return 0 on success otherwise a negative error code
 */
int snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info)
{
assert(ctl && info);
return ctl->ops->card_info(ctl, info);
}
// -------------------------snd_ctl_card_info---------------------------------------
dev = -1;
while (1) {
unsigned int count;
if (snd_ctl_pcm_next_device(handle, &dev)<0)
LOGD("snd_ctl_pcm_next_device");
if (dev < 0)
break;
snd_pcm_info_set_device(pcminfo, dev);
snd_pcm_info_set_subdevice(pcminfo, 0);
snd_pcm_info_set_stream(pcminfo, stream);
if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
if (err != -ENOENT)
LOGD("control digital audio info (%i): %s", card, snd_strerror(err));
continue;
}

LOGD("card %i: %s [%s], device %i: %s [%s]\n",
card, snd_ctl_card_info_get_id(info), snd_ctl_card_info_get_name(info),
dev,
snd_pcm_info_get_id(pcminfo),
snd_pcm_info_get_name(pcminfo));
                
if(strcmp(snd_pcm_info_get_id(pcminfo),"IMX SPDIF mxc spdif-0")==0) {
    if(card_device==0)  sprintf(spdifcardname, "hw:0%d", card);
    else         sprintf(spdifcardname, "hw:%d,%d", card, dev);
    havespdifdevice =  true;
}
            
if(strcmp(snd_pcm_info_get_id(pcminfo),"SGTL5000 SGTL5000-0")==0) {
    if(card_device==0) sprintf(sgtlcardname, "hw:0%d", card);
    else               sprintf(sgtlcardname, "hw:%d,%d", card, dev);
    havesgtldevice =  true;                
}
cardnum++;
}
snd_ctl_close(handle);
next_card:


if (snd_card_next(&card) < 0) {
LOGD("snd_card_next");
break;
}
}
        


    property_get("ro.HDMI_AUDIO_OUTPUT", value, "");
    if((device & AudioSystem::DEVICE_OUT_WIRED_HDMI) && havespdifdevice && (strcmp(value, "1") == 0))
    {
        return spdifcardname;


    }else if(havesgtldevice)
    {
        return sgtlcardname;
    }
    
    return "default";
}
// --------------------------deviceName--------------------------------------


    // The PCM stream is opened in blocking mode, per ALSA defaults.  The
    // AudioFlinger seems to assume blocking mode too, so asynchronous mode
    // should not be used.
    int err = snd_pcm_open(&handle->handle, devName, direction(handle), 0);
// ++++++++++++++++++++++++snd_pcm_open++++++++++++++++++++++++++++++++++++++++\
// 想到了前面的函数snd_ctl_open,呜呼!危乎多哉!
/**
 * \brief Opens a PCM
 * \param pcmp Returned PCM handle
 * \param name ASCII identifier of the PCM handle
 * \param stream Wanted stream
 * \param mode Open mode (see #SND_PCM_NONBLOCK, #SND_PCM_ASYNC)
 * \return 0 on success otherwise a negative error code
 */
int snd_pcm_open(snd_pcm_t **pcmp, const char *name, 
snd_pcm_stream_t stream, int mode)
{
int err;
assert(pcmp && name);
// 这个函数已经看过了
err = snd_config_update();
if (err < 0)
return err;
return snd_pcm_open_noupdate(pcmp, snd_config, name, stream, mode, 0);
// +++++++++++++++++++++++++++snd_pcm_open_noupdate+++++++++++++++++++++++++++++++++++++
static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root,
const char *name, snd_pcm_stream_t stream,
int mode, int hop)
{
int err;
snd_config_t *pcm_conf;
const char *str;


err = snd_config_search_definition(root, "pcm", name, &pcm_conf);
if (err < 0) {
SNDERR("Unknown PCM %s", name);
return err;
}
if (snd_config_get_string(pcm_conf, &str) >= 0)
err = snd_pcm_open_noupdate(pcmp, root, str, stream, mode,
   hop + 1);
else {
snd_config_set_hop(pcm_conf, hop);
// ++++++++++++++++++++++++snd_config_set_hop++++++++++++++++++++++++++++++++++++++++
void snd_config_set_hop(snd_config_t *conf, int hop)
{
conf->hop = hop;
}
// ------------------------snd_config_set_hop----------------------------------------
err = snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode);
// ++++++++++++++++++++++++++++++snd_pcm_open_conf++++++++++++++++++++++++++++++++++
// 想起来一个与其非常相似的函数:snd_ctl_open_conf。
// 调用的函数上面都有介绍过,此处就不再说了。
static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,
    snd_config_t *pcm_root, snd_config_t *pcm_conf,
    snd_pcm_stream_t stream, int mode)
{
const char *str;
char *buf = NULL, *buf1 = NULL;
int err;
snd_config_t *conf, *type_conf = NULL;
snd_config_iterator_t i, next;
const char *id;
const char *lib = NULL, *open_name = NULL;
int (*open_func)(snd_pcm_t **, const char *, 
snd_config_t *, snd_config_t *, 
snd_pcm_stream_t, int) = NULL;
#ifndef PIC
extern void *snd_pcm_open_symbols(void);
#endif
void *h = NULL;
if (snd_config_get_type(pcm_conf) != SND_CONFIG_TYPE_COMPOUND) {
char *val;
id = NULL;
snd_config_get_id(pcm_conf, &id);
val = NULL;
snd_config_get_ascii(pcm_conf, &val);
SNDERR("Invalid type for PCM %s%sdefinition (id: %s, value: %s)", name ? name : "", name ? " " : "", id, val);
free(val);
return -EINVAL;
}
err = snd_config_search(pcm_conf, "type", &conf);
if (err < 0) {
SNDERR("type is not defined");
return err;
}
err = snd_config_get_id(conf, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(pcm_root, "pcm_type", str, &type_conf);
if (err >= 0) {
if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) {
SNDERR("Invalid type for PCM type %s definition", str);
goto _err;
}
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {
err = snd_config_get_string(n, &lib);
if (err < 0) {
SNDERR("Invalid type for %s", id);
goto _err;
}
continue;
}
if (strcmp(id, "open") == 0) {
err = snd_config_get_string(n, &open_name);
if (err < 0) {
SNDERR("Invalid type for %s", id);
goto _err;
}
continue;
}
SNDERR("Unknown field %s", id);
err = -EINVAL;
goto _err;
}
}
if (!open_name) {
buf = malloc(strlen(str) + 32);
if (buf == NULL) {
err = -ENOMEM;
goto _err;
}
open_name = buf;
sprintf(buf, "_snd_pcm_%s_open", str);
}
if (!lib) {
const char *const *build_in = build_in_pcms;
while (*build_in) {
if (!strcmp(*build_in, str))
break;
build_in++;
}
if (*build_in == NULL) {
buf1 = malloc(strlen(str) + sizeof(ALSA_PLUGIN_DIR) + 32);
if (buf1 == NULL) {
err = -ENOMEM;
goto _err;
}
lib = buf1;
sprintf(buf1, "%s/libasound_module_pcm_%s.so", ALSA_PLUGIN_DIR, str);
}
}
#ifndef PIC
snd_pcm_open_symbols();/* this call is for static linking only */
#endif
open_func = snd_dlobj_cache_lookup(open_name);
if (open_func) {
err = 0;
goto _err;
}
h = snd_dlopen(lib, RTLD_NOW);
if (h)
open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION));
err = 0;
if (!h) {
SNDERR("Cannot open shared library %s",
      lib ? lib : "[builtin]");
err = -ENOENT;
} else if (!open_func) {
SNDERR("symbol %s is not defined inside %s", open_name,
      lib ? lib : "[builtin]");
snd_dlclose(h);
err = -ENXIO;
}
       _err:
if (err >= 0) {
err = open_func(pcmp, name, pcm_root, pcm_conf, stream, mode);
if (err >= 0) {
if (h /*&& (mode & SND_PCM_KEEP_ALIVE)*/) {
snd_dlobj_cache_add(open_name, h, open_func);
h = NULL;
}
(*pcmp)->dl_handle = h;
err = 0;
} else {
if (h)
snd_dlclose(h);
}
}
if (type_conf)
snd_config_delete(type_conf);
free(buf);
free(buf1);
return err;
}
// ------------------------------snd_pcm_open_conf----------------------------------
}
snd_config_delete(pcm_conf);
return err;
}
// ---------------------------snd_pcm_open_noupdate-------------------------------------
}
// ------------------------snd_pcm_open----------------------------------------


    if (err < 0) {
        LOGE("Failed to Initialize any ALSA %s device: %s", stream, strerror(err));
        return NO_INIT;
    }


    err = setHardwareParams(handle);


    if (err == NO_ERROR) err = setSoftwareParams(handle);




    LOGI("Initialized ALSA %s device %s", stream, devName);


    handle->curDev = devices;
    handle->curMode = mode;


    return err;
}
// -----------------------------s_open-----------------------------------
            if (err) break;
            if (devices & AudioSystem::DEVICE_OUT_WIRED_HDMI){
                strcpy(mCurCard ,SPDIF);
                mMixer = mMixerSpdif;
            } else {
                strcpy(mCurCard,SGTL5000);
                mMixer = mMixerSgtl5000;
            }


            out = new AudioStreamOutALSA(this, &(*it));
            err = out->set(format, channels, sampleRate);
            break;
        }
// ----------------------------------------------------------------

/*
至此,it的来历基本上清楚了。
下面看看函数snd_pcm_mmap_writei和snd_pcm_writei的实现。


回忆一下上面的代码:
        if (mHandle->mmap)
            n = snd_pcm_mmap_writei(mHandle->handle,
                               (char *)buffer + sent,
                               snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent));
else
            n = snd_pcm_writei(mHandle->handle,
                               (char *)buffer + sent,
                               snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent));
  */
// +++++++++++++++++++++++++snd_pcm_mmap_writei+++++++++++++++++++++++++++++++++++++++
/**
 * \brief Write interleaved frames to a PCM using direct buffer (mmap)
 * \param pcm PCM handle
 * \param buffer frames containing buffer
 * \param size frames to be written
 * \return a positive number of frames actually written otherwise a
 * negative error code
 * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING)
 * \retval -EPIPE an underrun occurred
 * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)
 *
 * If the blocking behaviour is selected, then routine waits until
 * all requested bytes are played or put to the playback ring buffer.
 * The count of bytes can be less only if a signal or underrun occurred.
 *
 * If the non-blocking behaviour is selected, then routine doesn't wait at all.
 */
snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
snd_pcm_channel_area_t areas[pcm->channels];
snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
return snd_pcm_write_areas(pcm, areas, 0, size,
  snd_pcm_mmap_write_areas);
// ++++++++++++++++++++++++++++snd_pcm_write_areas++++++++++++++++++++++++++++++++++++
snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
     snd_pcm_uframes_t offset, snd_pcm_uframes_t size,
     snd_pcm_xfer_areas_func_t func)
{
snd_pcm_uframes_t xfer = 0;
snd_pcm_sframes_t err = 0;
snd_pcm_state_t state;


if (size == 0)
return 0;


while (size > 0) {
snd_pcm_uframes_t frames;
snd_pcm_sframes_t avail;
_again:
state = snd_pcm_state(pcm);
// ++++++++++++++++++++++++++++++snd_pcm_state++++++++++++++++++++++++++++++++++
/**
 * \brief Return PCM state
 * \param pcm PCM handle
 * \return PCM state #snd_pcm_state_t of given PCM handle
 *
 * This is a faster way to obtain only the PCM state without calling
 * \link ::snd_pcm_status() \endlink.
 */
snd_pcm_state_t snd_pcm_state(snd_pcm_t *pcm)
{
assert(pcm);
return pcm->fast_ops->state(pcm->fast_op_arg);
}
// ------------------------------snd_pcm_state----------------------------------
switch (state) {
case SND_PCM_STATE_PREPARED:
case SND_PCM_STATE_PAUSED:
break;
case SND_PCM_STATE_RUNNING:
err = snd_pcm_hwsync(pcm);
if (err < 0)
goto _end;
// +++++++++++++++++++++++++++snd_pcm_hwsync+++++++++++++++++++++++++++++++++++++
/**
 * \brief (DEPRECATED) Synchronize stream position with hardware
 * \param pcm PCM handle
 * \return 0 on success otherwise a negative error code
 *
 * Note this function does not update the actual r/w pointer
 * for applications. The function #snd_pcm_avail_update()
 * have to be called before any mmap begin+commit operation.
 */
int snd_pcm_hwsync(snd_pcm_t *pcm)
{
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
return pcm->fast_ops->hwsync(pcm->fast_op_arg);
}
// ---------------------------snd_pcm_hwsync-------------------------------------
break;
case SND_PCM_STATE_XRUN:
err = -EPIPE;
goto _end;
case SND_PCM_STATE_SUSPENDED:
err = -ESTRPIPE;
goto _end;
case SND_PCM_STATE_DISCONNECTED:
err = -ENODEV;
goto _end;
default:
err = -EBADFD;
goto _end;
}
avail = snd_pcm_avail_update(pcm);
if (avail < 0) {
err = avail;
goto _end;
}
// +++++++++++++++++++++++++++++snd_pcm_avail_update+++++++++++++++++++++++++++++++++++
/**
 * \brief Return number of frames ready to be read (capture) / written (playback)
 * \param pcm PCM handle
 * \return a positive number of frames ready otherwise a negative
 * error code
 *
 * On capture does all the actions needed to transport to application
 * level all the ready frames across underlying layers.
 *
 * The position is not synced with hardware (driver) position in the sound
 * ring buffer in this function. This function is a light version of
 * #snd_pcm_avail() .
 *
 * Using this function is ideal after poll() or select() when audio
 * file descriptor made the event and when application expects just period
 * timing.
 *
 * Also this function might be called after #snd_pcm_delay() or
 * #snd_pcm_hwsync() functions to move private ring buffer pointers
 * in alsa-lib (the internal plugin chain).
 */
snd_pcm_sframes_t snd_pcm_avail_update(snd_pcm_t *pcm)
{
return pcm->fast_ops->avail_update(pcm->fast_op_arg);
}
// -----------------------------snd_pcm_avail_update-----------------------------------
if ((state == SND_PCM_STATE_RUNNING &&
    (snd_pcm_uframes_t)avail < pcm->avail_min &&
    size > (snd_pcm_uframes_t)avail)) {
if (pcm->mode & SND_PCM_NONBLOCK) {
err = -EAGAIN;
goto _end;
}


err = snd_pcm_wait(pcm, -1);
if (err < 0)
break;
goto _again; 
// +++++++++++++++++++++++++++snd_pcm_wait+++++++++++++++++++++++++++++++++++++
/**
 * \brief Wait for a PCM to become ready
 * \param pcm PCM handle
 * \param timeout maximum time in milliseconds to wait,
 *        a negative value means infinity
 * \return a positive value on success otherwise a negative error code
 *         (-EPIPE for the xrun and -ESTRPIPE for the suspended status,
 *          others for general errors) 
 * \retval 0 timeout occurred
 * \retval 1 PCM stream is ready for I/O
 */
int snd_pcm_wait(snd_pcm_t *pcm, int timeout)
{
if (snd_pcm_mmap_avail(pcm) >= pcm->avail_min) {
/* check more precisely */
switch (snd_pcm_state(pcm)) {
case SND_PCM_STATE_XRUN:
return -EPIPE;
case SND_PCM_STATE_SUSPENDED:
return -ESTRPIPE;
case SND_PCM_STATE_DISCONNECTED:
return -ENODEV;
default:
return 1;
}
}
// +++++++++++++++++++++++++snd_pcm_mmap_avail+++++++++++++++++++++++++++++++++++++++
static inline snd_pcm_uframes_t snd_pcm_mmap_avail(snd_pcm_t *pcm)
{
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
// 先只看播放的
return snd_pcm_mmap_playback_avail(pcm);
// ++++++++++++++++++++++++++++snd_pcm_mmap_playback_avail++++++++++++++++++++++++++++++++++++
static inline snd_pcm_uframes_t snd_pcm_mmap_playback_avail(snd_pcm_t *pcm)
{
snd_pcm_sframes_t avail;
avail = *pcm->hw.ptr + pcm->buffer_size - *pcm->appl.ptr;
if (avail < 0)
avail += pcm->boundary;
else if ((snd_pcm_uframes_t) avail >= pcm->boundary)
avail -= pcm->boundary;
return avail;
}
// ----------------------------snd_pcm_mmap_playback_avail------------------------------------
else
return snd_pcm_mmap_capture_avail(pcm);
}
// --------------------------snd_pcm_mmap_avail--------------------------------------
return snd_pcm_wait_nocheck(pcm, timeout);
}
// ----------------------------snd_pcm_wait------------------------------------
}
frames = size;
if (frames > (snd_pcm_uframes_t) avail)
frames = avail;
if (! frames)
break;
// func是传入的参数snd_pcm_mmap_write_areas
err = func(pcm, areas, offset, frames);
// +++++++++++++++++++++++++++func是传入的参数snd_pcm_mmap_write_areas+++++++++++++++++++++++++++++++++++++
static snd_pcm_sframes_t snd_pcm_mmap_write_areas(snd_pcm_t *pcm,
 const snd_pcm_channel_area_t *areas,
 snd_pcm_uframes_t offset,
 snd_pcm_uframes_t size)
{
snd_pcm_uframes_t xfer = 0;


if (snd_pcm_mmap_playback_avail(pcm) < size) {
SNDMSG("too short avail %ld to size %ld", snd_pcm_mmap_playback_avail(pcm), size);
return -EPIPE;
}
while (size > 0) {
const snd_pcm_channel_area_t *pcm_areas;
snd_pcm_uframes_t pcm_offset;
snd_pcm_uframes_t frames = size;
snd_pcm_sframes_t result;


snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
// +++++++++++++++++++++++++snd_pcm_mmap_begin+++++++++++++++++++++++++++++++++++++++
/**
 * \brief Application request to access a portion of direct (mmap) area
 * \param pcm PCM handle 
 * \param areas Returned mmap channel areas
 * \param offset Returned mmap area offset in area steps (== frames)
 * \param frames mmap area portion size in frames (wanted on entry, contiguous available on exit)
 * \return 0 on success otherwise a negative error code
 *
 * It is necessary to call the snd_pcm_avail_update() function directly before
 * this call. Otherwise, this function can return a wrong count of available frames.
 *
 * The function should be called before a sample-direct area can be accessed.
 * The resulting size parameter is always less or equal to the input count of frames
 * and can be zero, if no frames can be processed (the ring buffer is full).
 *
 * See the snd_pcm_mmap_commit() function to finish the frame processing in
 * the direct areas.
 */
int snd_pcm_mmap_begin(snd_pcm_t *pcm,
      const snd_pcm_channel_area_t **areas,
      snd_pcm_uframes_t *offset,
      snd_pcm_uframes_t *frames)
{
snd_pcm_uframes_t cont;
snd_pcm_uframes_t f;
snd_pcm_uframes_t avail;
const snd_pcm_channel_area_t *xareas;
assert(pcm && areas && offset && frames);
xareas = snd_pcm_mmap_areas(pcm);
if (xareas == NULL)
return -EBADFD;
// +++++++++++++++++++++++++++snd_pcm_mmap_areas+++++++++++++++++++++++++++++++++++++
static inline const snd_pcm_channel_area_t *snd_pcm_mmap_areas(snd_pcm_t *pcm)
{
if (pcm->stopped_areas &&
   snd_pcm_state(pcm) != SND_PCM_STATE_RUNNING) 
return pcm->stopped_areas;
return pcm->running_areas;
}
// ---------------------------snd_pcm_mmap_areas-------------------------------------
*areas = xareas;
*offset = *pcm->appl.ptr % pcm->buffer_size;
avail = snd_pcm_mmap_avail(pcm);
if (avail > pcm->buffer_size)
avail = pcm->buffer_size;
cont = pcm->buffer_size - *offset;
f = *frames;
if (f > avail)
f = avail;
if (f > cont)
f = cont;
*frames = f;
return 0;
}
// -------------------------snd_pcm_mmap_begin---------------------------------------
// 把数据从areas copy 到 pcm_areas
// areas是传入的参数
// pcm_areas是pcm(snd_pcm_t)的stopped_areas或者running_areas。
snd_pcm_areas_copy(pcm_areas, pcm_offset,
  areas, offset, 
  pcm->channels, 
  frames, pcm->format);
// +++++++++++++++++++++++++snd_pcm_areas_copy+++++++++++++++++++++++++++++++++++++++
/**
 * \brief Copy one or more areas
 * \param dst_areas destination areas specification (one for each channel)
 * \param dst_offset offset in frames inside destination area
 * \param src_areas source areas specification (one for each channel)
 * \param src_offset offset in frames inside source area
 * \param channels channels count
 * \param frames frames to copy
 * \param format PCM sample format
 * \return 0 on success otherwise a negative error code
 */
int snd_pcm_areas_copy(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
      const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
      unsigned int channels, snd_pcm_uframes_t frames, snd_pcm_format_t format)
{
int width = snd_pcm_format_physical_width(format);
assert(dst_areas);
assert(src_areas);
if (! channels) {
SNDMSG("invalid channels %d", channels);
return -EINVAL;
}
if (! frames) {
SNDMSG("invalid frames %ld", frames);
return -EINVAL;
}
while (channels > 0) {
unsigned int step = src_areas->step;
void *src_addr = src_areas->addr;
const snd_pcm_channel_area_t *src_start = src_areas;
void *dst_addr = dst_areas->addr;
const snd_pcm_channel_area_t *dst_start = dst_areas;
int channels1 = channels;
unsigned int chns = 0;
while (dst_areas->step == step) {
channels1--;
chns++;
src_areas++;
dst_areas++;
if (channels1 == 0 ||
   src_areas->step != step ||
   src_areas->addr != src_addr ||
   dst_areas->addr != dst_addr ||
   src_areas->first != src_areas[-1].first + width ||
   dst_areas->first != dst_areas[-1].first + width)
break;
}
if (chns > 1 && chns * width == step) {
/* Collapse the areas */
snd_pcm_channel_area_t s, d;
s.addr = src_start->addr;
s.first = src_start->first;
s.step = width;
d.addr = dst_start->addr;
d.first = dst_start->first;
d.step = width;
snd_pcm_area_copy(&d, dst_offset * chns,
 &s, src_offset * chns, 
 frames * chns, format);
channels -= chns;
// ++++++++++++++++++++++++++++snd_pcm_area_copy++++++++++++++++++++++++++++++++++++
/**
 * \brief Copy an area
 * \param dst_area destination area specification
 * \param dst_offset offset in frames inside destination area
 * \param src_area source area specification
 * \param src_offset offset in frames inside source area
 * \param samples samples to copy
 * \param format PCM sample format
 * \return 0 on success otherwise a negative error code
 */
int snd_pcm_area_copy(const snd_pcm_channel_area_t *dst_area, snd_pcm_uframes_t dst_offset,
     const snd_pcm_channel_area_t *src_area, snd_pcm_uframes_t src_offset,
     unsigned int samples, snd_pcm_format_t format)
{
/* FIXME: sub byte resolution and odd dst_offset */
const char *src;
char *dst;
int width;
int src_step, dst_step;
if (dst_area == src_area && dst_offset == src_offset)
return 0;
if (!src_area->addr)
return snd_pcm_area_silence(dst_area, dst_offset, samples, format);
src = snd_pcm_channel_area_addr(src_area, src_offset);
if (!dst_area->addr)
return 0;
// +++++++++++++++++++++++snd_pcm_channel_area_addr+++++++++++++++++++++++++++++++++++++++++
static inline void *snd_pcm_channel_area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset)
{
unsigned int bitofs = area->first + area->step * offset;
assert(bitofs % 8 == 0);
return (char *) area->addr + bitofs / 8;
}
// -----------------------snd_pcm_channel_area_addr-----------------------------------------
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
width = snd_pcm_format_physical_width(format);
if (src_area->step == (unsigned int) width &&
   dst_area->step == (unsigned int) width) {
size_t bytes = samples * width / 8;
samples -= bytes * 8 / width;
memcpy(dst, src, bytes);
if (samples == 0)
return 0;
}
src_step = src_area->step / 8;
dst_step = dst_area->step / 8;
switch (width) {
case 4: {
int srcbit = src_area->first % 8;
int srcbit_step = src_area->step % 8;
int dstbit = dst_area->first % 8;
int dstbit_step = dst_area->step % 8;
while (samples-- > 0) {
unsigned char srcval;
if (srcbit)
srcval = *src & 0x0f;
else
srcval = *src & 0xf0;
if (dstbit)
*dst &= 0xf0;
else
*dst &= 0x0f;
*dst |= srcval;
src += src_step;
srcbit += srcbit_step;
if (srcbit == 8) {
src++;
srcbit = 0;
}
dst += dst_step;
dstbit += dstbit_step;
if (dstbit == 8) {
dst++;
dstbit = 0;
}
}
break;
}
case 8: {
while (samples-- > 0) {
*dst = *src;
src += src_step;
dst += dst_step;
}
break;
}
case 16: {
while (samples-- > 0) {
*(u_int16_t*)dst = *(const u_int16_t*)src;
src += src_step;
dst += dst_step;
}
break;
}
case 24:
while (samples-- > 0) {
*(dst + 0) = *(src + 0);
*(dst + 1) = *(src + 1);
*(dst + 2) = *(src + 2);
src += src_step;
dst += dst_step;
}
break;
case 32: {
while (samples-- > 0) {
*(u_int32_t*)dst = *(const u_int32_t*)src;
src += src_step;
dst += dst_step;
}
break;
}
case 64: {
while (samples-- > 0) {
*(u_int64_t*)dst = *(const u_int64_t*)src;
src += src_step;
dst += dst_step;
}
break;
}
default:
SNDMSG("invalid format width %d", width);
return -EINVAL;
}
return 0;
}
// ----------------------------snd_pcm_area_copy------------------------------------
} else {
snd_pcm_area_copy(dst_start, dst_offset,
 src_start, src_offset,
 frames, format);
src_areas = src_start + 1;
dst_areas = dst_start + 1;
channels--;
}
}
return 0;
}
// -------------------------snd_pcm_areas_copy---------------------------------------
result = snd_pcm_mmap_commit(pcm, pcm_offset, frames);
if (result < 0)
return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
// +++++++++++++++++++++++++++snd_pcm_mmap_commit+++++++++++++++++++++++++++++++++++++
/**
 * \brief Application has completed the access to area requested with #snd_pcm_mmap_begin
 * \param pcm PCM handle
 * \param offset area offset in area steps (== frames)
 * \param frames area portion size in frames
 * \return count of transferred frames otherwise a negative error code
 *
 * You should pass this function the offset value that
 * snd_pcm_mmap_begin() returned. The frames parameter should hold the
 * number of frames you have written or read to/from the audio
 * buffer. The frames parameter must never exceed the contiguous frames
 * count that snd_pcm_mmap_begin() returned. Each call to snd_pcm_mmap_begin()
 * must be followed by a call to snd_pcm_mmap_commit().
 *
 * Example:
\code
  double phase = 0;
  const snd_pcm_area_t *areas;
  snd_pcm_sframes_t avail, size, commitres;
  snd_pcm_uframes_t offset, frames;
  int err;


  avail = snd_pcm_avail_update(pcm);
  if (avail < 0)
    error(avail);
  // at this point, we can transfer at least 'avail' frames
  
  // we want to process frames in chunks (period_size)
  if (avail < period_size)
    goto _skip;
  size = period_size;
  // it is possible that contiguous areas are smaller, thus we use a loop
  while (size > 0) {
    frames = size;


    err = snd_pcm_mmap_begin(pcm_handle, &areas, &offset, &frames);
    if (err < 0)
      error(err);
    // this function fills the areas from offset with count of frames
    generate_sine(areas, offset, frames, &phase);
    commitres = snd_pcm_mmap_commit(pcm_handle, offset, frames);
    if (commitres < 0 || commitres != frames)
      error(commitres >= 0 ? -EPIPE : commitres);
      
    size -= frames;
  }
 _skip:
\endcode
 *
 * Look to the \ref example_test_pcm "Sine-wave generator" example
 * for more details about the generate_sine function.
 */
snd_pcm_sframes_t snd_pcm_mmap_commit(snd_pcm_t *pcm,
     snd_pcm_uframes_t offset,
     snd_pcm_uframes_t frames)
{
assert(pcm);
if (CHECK_SANITY(offset != *pcm->appl.ptr % pcm->buffer_size)) {
SNDMSG("commit offset (%ld) doesn't match with appl_ptr (%ld) %% buf_size (%ld)",
      offset, *pcm->appl.ptr, pcm->buffer_size);
return -EPIPE;
}
if (CHECK_SANITY(frames > snd_pcm_mmap_avail(pcm))) {
SNDMSG("commit frames (%ld) overflow (avail = %ld)", frames,
      snd_pcm_mmap_avail(pcm));
return -EPIPE;
}
return pcm->fast_ops->mmap_commit(pcm->fast_op_arg, offset, frames);
}
// ---------------------------snd_pcm_mmap_commit-------------------------------------
offset += result;
xfer += result;
size -= result;
}
return (snd_pcm_sframes_t)xfer;
}
// ---------------------------func是传入的参数snd_pcm_mmap_write_areas-------------------------------------
if (err < 0)
break;
frames = err;
if (state == SND_PCM_STATE_PREPARED) {
snd_pcm_sframes_t hw_avail = pcm->buffer_size - avail;
hw_avail += frames;
/* some plugins might automatically start the stream */
state = snd_pcm_state(pcm);
if (state == SND_PCM_STATE_PREPARED &&
   hw_avail >= (snd_pcm_sframes_t) pcm->start_threshold) {
err = snd_pcm_start(pcm);
if (err < 0)
goto _end;
}
}
offset += frames;
size -= frames;
xfer += frames;
}
 _end:
return xfer > 0 ? (snd_pcm_sframes_t) xfer : snd_pcm_check_error(pcm, err);
}
// ----------------------------snd_pcm_write_areas------------------------------------
}
// -------------------------snd_pcm_mmap_writei---------------------------------------


// ++++++++++++++++++++++++++snd_pcm_writei++++++++++++++++++++++++++++++++++++++
/**
 * \brief Write interleaved frames to a PCM
 * \param pcm PCM handle
 * \param buffer frames containing buffer
 * \param size frames to be written
 * \return a positive number of frames actually written otherwise a
 * negative error code
 * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING)
 * \retval -EPIPE an underrun occurred
 * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)
 *
 * If the blocking behaviour is selected and it is running, then routine waits until
 * all requested frames are played or put to the playback ring buffer.
 * The returned number of frames can be less only if a signal or underrun occurred.
 *
 * If the non-blocking behaviour is selected, then routine doesn't wait at all.
 */ 
snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
assert(pcm);
assert(size == 0 || buffer);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
if (pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED) {
SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access));
return -EINVAL;
}
return _snd_pcm_writei(pcm, buffer, size);
// +++++++++++++++++++++++++++_snd_pcm_writei+++++++++++++++++++++++++++++++++++++
static inline snd_pcm_sframes_t _snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
return pcm->fast_ops->writei(pcm->fast_op_arg, buffer, size);
}
// ---------------------------_snd_pcm_writei-------------------------------------
}
// --------------------------snd_pcm_writei--------------------------------------
// ----------------------------------------------------------------
        if (n == -EBADFD) {
            // Somehow the stream is in a bad state. The driver probably
            // has a bug and snd_pcm_recover() doesn't seem to handle this.
            mHandle->module->open(mHandle, mHandle->curDev, mHandle->curMode);


            if (aDev && aDev->recover) aDev->recover(aDev, n);
        }
        else if (n < 0) {
            if (mHandle->handle) {
LOGW("underrun and do recovery.....");
                // snd_pcm_recover() will return 0 if successful in recovering from
                // an error, or -errno if the error was unrecoverable.
                n = snd_pcm_recover(mHandle->handle, n, 1);


                if (aDev && aDev->recover) aDev->recover(aDev, n);


                if (n) return static_cast<ssize_t>(n);
            }
        }
        else {
            mFrameCount += n;
            sent += static_cast<ssize_t>(snd_pcm_frames_to_bytes(mHandle->handle, n));
        }


    } while (mHandle->handle && sent < bytes);


    return sent;
}


###########################################################


&&&&&&&&&&&&&&&&&&&&&&&总结&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
在open output stream的时候,会创建一个snd_pcm_t的结构体。
write数据的时候,有两中方式:
1、mmap方式,其实是往结构体的stopped_areas或者running_areas成员中copy数据。
写完后,通过commit函数通知底层:
pcm->fast_ops->mmap_commit
2、否则,直接调用结构体的writei函数写数据:
pcm->fast_ops->writei
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics