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
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&