Android Audioコード解析11-AudioStreamOutALSA::write関数

81422 ワード

オーディオデータがどのように書かれているかを見ると、MixerThreadのthreadloop関数では、次のコードがハードウェアへのデータの書き込みを完了しています.
int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
mOutputの由来:
関数AudioFlinger::openOutputでMixerThreadオブジェクトを作成し、mAudioHardware->openOutputStreamを前に呼び出したoutputをパラメータとして入力します.
MixerThreadはPlaybackThreadから継承され、PlaybackThreadのコンストラクション関数で入力されたoutputがmOutputに割り当てられます.
関数AudioFlinger::openOutputは以前から付き合っていました.mAudioHardwareは実はAudioHardware ALSAオブジェクトです.
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;
}

*******************************************************************************************************************************************************************************************************************************************************************************************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 ****
", 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]
", 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; }

######################################################&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&pcm_tの構造体.writeデータの場合、2つの方法があります:1、mmap方式、実は構造体へのstopped_areasまたはrunning_areasメンバーのcopyデータ.書き終わったら、commit関数で最下位に通知します:pcm->fast_ops->mmap_commit 2、そうでなければ、構造体のwritei関数を直接呼び出してデータを書きます:pcm->fast_ops->writei &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&