http://blog.csdn.net/hgl868/article/details/6872132
在看音频数据是怎么写的时候,在MixerThread的threadloop函数中,有以下代码完成了往硬件写数据:
int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
mOutput来历:
函数AudioFlinger::openOutput中创建了一个MixerThread对象,并将前面调用mAudioHardware->openOutputStream得到的output作为参数传入。
MixerThread继承自PlaybackThread,在PlaybackThread的构造函数中将传入的output赋值给了mOutput。
函数AudioFlinger::openOutput以前已经打过交道。mAudioHardware其实是一个AudioHardwareALSA对象。
调用mAudioHardware->openOutputStream得到的其实是一个AudioStreamOutALSA对象。
所以,mOutput->write,其实就是函数AudioStreamOutALSA::write。
*****************************************源码*************************************************
ssize_t AudioStreamOutALSA::write(const void *buffer, size_t bytes)
{
AutoMutex lock(mLock);
if (!mPowerLock) {
acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock");
mPowerLock = true;
}
acoustic_device_t *aDev = acoustics();
// For output, we will pass the data on to the acoustics module, but the actual
// data is expected to be sent to the audio device directly as well.
if (aDev && aDev->write)
aDev->write(aDev, buffer, bytes);
snd_pcm_sframes_t n;
size_t sent = 0;
status_t err;
do {
if (mHandle->mmap)
n = snd_pcm_mmap_writei(mHandle->handle,
(char *)buffer + sent,
snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent));
else
n = snd_pcm_writei(mHandle->handle,
(char *)buffer + sent,
snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent));
if (n == -EBADFD) {
// Somehow the stream is in a bad state. The driver probably
// has a bug and snd_pcm_recover() doesn't seem to handle this.
mHandle->module->open(mHandle, mHandle->curDev, mHandle->curMode);
if (aDev && aDev->recover) aDev->recover(aDev, n);
}
else if (n < 0) {
if (mHandle->handle) {
LOGW("underrun and do recovery.....");
// snd_pcm_recover() will return 0 if successful in recovering from
// an error, or -errno if the error was unrecoverable.
n = snd_pcm_recover(mHandle->handle, n, 1);
if (aDev && aDev->recover) aDev->recover(aDev, n);
if (n) return static_cast<ssize_t>(n);
}
}
else {
mFrameCount += n;
sent += static_cast<ssize_t>(snd_pcm_frames_to_bytes(mHandle->handle, n));
}
} while (mHandle->handle && sent < bytes);
return sent;
}
**********************************************************************************************
源码路径:
hardware\alsa_sound\AudioStreamOutALSA.cpp
#######################说明################################
ssize_t AudioStreamOutALSA::write(const void *buffer, size_t bytes)
{
AutoMutex lock(mLock);
if (!mPowerLock) {
acquire_wake_lock (PARTIAL_WAKE_LOCK, "AudioOutLock");
mPowerLock = true;
}
// 看看acoustics是个什么东东
acoustic_device_t *aDev = acoustics();
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
acoustic_device_t *ALSAStreamOps::acoustics()
{
return mParent->mAcousticDevice;
}
ALSAStreamOps是AudioStreamOutALSA的父类。
mParent是在ALSAStreamOps的构造函数中赋值的。
AudioStreamOutALSA对象是在AudioHardwareALSA::openOutputStream中创建。
out = new AudioStreamOutALSA(this, &(*it));
在ALSAStreamOps的构造函数中,将传入的this赋值给了mParent。
mAcousticDevice其实是AudioHardwareALSA的成员变量。其赋值在AudioHardwareALSA的构造函数中完成:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
err = hw_get_module(ACOUSTICS_HARDWARE_MODULE_ID,
(hw_module_t const**)&module);
if (err == 0) {
hw_device_t* device;
err = module->methods->open(module, ACOUSTICS_HARDWARE_NAME, &device);
if (err == 0)
mAcousticDevice = (acoustic_device_t *)device;
else
LOGE("Acoustics Module not found.");
}
----------------------------------------------------------------
----------------------------------------------------------------
// For output, we will pass the data on to the acoustics module, but the actual
// data is expected to be sent to the audio device directly as well.
if (aDev && aDev->write)
aDev->write(aDev, buffer, bytes);
snd_pcm_sframes_t n;
size_t sent = 0;
status_t err;
do {
// 写数据是在这儿完成的。
// 有两个关键的东东需要确认:
// 1、mHandle是怎么来的。
// 2、函数snd_pcm_mmap_writei是怎么实现的。
if (mHandle->mmap)
n = snd_pcm_mmap_writei(mHandle->handle,
(char *)buffer + sent,
snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent));
else
n = snd_pcm_writei(mHandle->handle,
(char *)buffer + sent,
snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent));
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
先看看第一个问题:
1、mHandle是怎么来的。
mHandle也是在ALSAStreamOps的构造函数中赋值的。
所赋值其实就是以下代码中的it。
out = new AudioStreamOutALSA(this, &(*it));
it的来历:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Find the appropriate alsa device
// mALSADevice的产生,在看函数AudioHardwareALSA::openOutputStream的时候已经知道
for(ALSAHandleList::iterator it = mDeviceList.begin();
it != mDeviceList.end(); ++it)
if (it->devices & devices) {
// open函数以前也有见过,不过当时没有彻底啃干净
// 在看函数AudioHardwareALSA::openOutputStream的时候,了解到,此处其实是调用的函数s_open
err = mALSADevice->open(&(*it), devices, mode());
+++++++++++++++++++++++++++++s_open+++++++++++++++++++++++++++++++++++
static status_t s_open(alsa_handle_t *handle, uint32_t devices, int mode)
{
// Close off previously opened device.
// It would be nice to determine if the underlying device actually
// changes, but we might be recovering from an error or manipulating
// mixer settings (see asound.conf).
//
s_close(handle);
+++++++++++++++++++++++++++++s_close+++++++++++++++++++++++++++++++++++
static status_t s_close(alsa_handle_t *handle)
{
LOGW("s_close--");
status_t err = NO_ERROR;
snd_pcm_t *h = handle->handle;
handle->handle = 0;
handle->curDev = 0;
handle->curMode = 0;
if (h) {
snd_pcm_drain(h);
+++++++++++++++++++++++++++++snd_pcm_drain+++++++++++++++++++++++++++++++++++
/**
* \brief Stop a PCM preserving pending frames
* \param pcm PCM handle
* \return 0 on success otherwise a negative error code
* \retval -ESTRPIPE a suspend event occurred
*
* For playback wait for all pending frames to be played and then stop
* the PCM.
* For capture stop PCM permitting to retrieve residual frames.
*
* For stopping the PCM stream immediately, use \link ::snd_pcm_drop() \endlink
* instead.
*/
int snd_pcm_drain(snd_pcm_t *pcm)
{
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
// 调到了snd_pcm_t中的东东
// 又回到了原来的话题,alsa_handle_t结构体的来历
return pcm->fast_ops->drain(pcm->fast_op_arg);
}
-----------------------------snd_pcm_drain-----------------------------------
err = snd_pcm_close(h);
++++++++++++++++++++++++++++snd_pcm_close++++++++++++++++++++++++++++++++++++
/**
* \brief close PCM handle
* \param pcm PCM handle
* \return 0 on success otherwise a negative error code
*
* Closes the specified PCM handle and frees all associated
* resources.
*/
int snd_pcm_close(snd_pcm_t *pcm)
{
int res = 0, err;
assert(pcm);
if (pcm->setup && !pcm->donot_close) {
snd_pcm_drop(pcm);
+++++++++++++++++++++++++++snd_pcm_drop+++++++++++++++++++++++++++++++++++++
/**
* \brief Stop a PCM dropping pending frames
* \param pcm PCM handle
* \return 0 on success otherwise a negative error code
*
* This function stops the PCM <i>immediately</i>.
* The pending samples on the buffer are ignored.
*
* For processing all pending samples, use \link ::snd_pcm_drain() \endlink
* instead.
*/
int snd_pcm_drop(snd_pcm_t *pcm)
{
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
return pcm->fast_ops->drop(pcm->fast_op_arg);
}
---------------------------snd_pcm_drop-------------------------------------
err = snd_pcm_hw_free(pcm);
++++++++++++++++++++++++++++++snd_pcm_hw_free++++++++++++++++++++++++++++++++++
/** \brief Remove PCM hardware configuration and free associated resources
* \param pcm PCM handle
* \return 0 on success otherwise a negative error code
*/
int snd_pcm_hw_free(snd_pcm_t *pcm)
{
int err;
if (! pcm->setup)
return 0;
if (pcm->mmap_channels) {
err = snd_pcm_munmap(pcm);
+++++++++++++++++++++++++snd_pcm_munmap+++++++++++++++++++++++++++++++++++++++
int snd_pcm_munmap(snd_pcm_t *pcm)
{
int err;
unsigned int c;
assert(pcm);
if (CHECK_SANITY(! pcm->mmap_channels)) {
SNDMSG("Not mmapped");
return -ENXIO;
}
if (pcm->mmap_shadow)
return pcm->ops->munmap(pcm);
for (c = 0; c < pcm->channels; ++c) {
snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
unsigned int c1;
size_t size = i->first + i->step * (pcm->buffer_size - 1) + pcm->sample_bits;
if (!i->addr)
continue;
for (c1 = c + 1; c1 < pcm->channels; ++c1) {
snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
size_t s;
if (i1->addr != i->addr)
continue;
i1->addr = NULL;
s = i1->first + i1->step * (pcm->buffer_size - 1) + pcm->sample_bits;
if (s > size)
size = s;
}
size = (size + 7) / 8;
size = page_align(size);
switch (i->type) {
case SND_PCM_AREA_MMAP:
err = munmap(i->addr, size);
if (err < 0) {
SYSERR("mmap failed");
return -errno;
}
errno = 0;
break;
#ifndef ANDROID
case SND_PCM_AREA_SHM:
if (i->u.shm.area) {
snd_shm_area_destroy(i->u.shm.area);
+++++++++++++++++++++++snd_shm_area_destroy+++++++++++++++++++++++++++++++++++++++++
/**
* \brief Release the shared area record
* \param area the shared are record
* \return 0 if successful, or a negative error code
*
* Decreases the reference counter of the given shared area record, and
* releases the resources automaticall if it reaches to 0.
*/
int snd_shm_area_destroy(struct snd_shm_area *area)
{
if (area == NULL)
return -ENOENT;
if (--area->share)
return 0;
list_del(&area->list);
shmdt(area->ptr);
free(area);
return 0;
}
-----------------------snd_shm_area_destroy-----------------------------------------
i->u.shm.area = NULL;
if (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
unsigned int c1;
for (c1 = c + 1; c1 < pcm->channels; c1++) {
snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
if (i1->u.shm.area) {
snd_shm_area_destroy(i1->u.shm.area);
i1->u.shm.area = NULL;
}
}
}
}
break;
#endif
case SND_PCM_AREA_LOCAL:
free(i->addr);
break;
default:
assert(0);
}
i->addr = NULL;
}
err = pcm->ops->munmap(pcm);
if (err < 0)
return err;
free(pcm->mmap_channels);
free(pcm->running_areas);
pcm->mmap_channels = NULL;
pcm->running_areas = NULL;
return 0;
}
-------------------------snd_pcm_munmap---------------------------------------
if (err < 0)
return err;
}
// assert(snd_pcm_state(pcm) == SND_PCM_STATE_SETUP ||
// snd_pcm_state(pcm) == SND_PCM_STATE_PREPARED);
err = pcm->ops->hw_free(pcm->op_arg);
pcm->setup = 0;
if (err < 0)
return err;
return 0;
}
------------------------------snd_pcm_hw_free----------------------------------
if (err < 0)
res = err;
}
if (pcm->mmap_channels)
snd_pcm_munmap(pcm);
while (!list_empty(&pcm->async_handlers)) {
snd_async_handler_t *h = list_entry(pcm->async_handlers.next, snd_async_handler_t, hlist);
snd_async_del_handler(h);
}
err = pcm->ops->close(pcm->op_arg);
if (err < 0)
res = err;
err = snd_pcm_free(pcm);
if (err < 0)
res = err;
return res;
}
----------------------------snd_pcm_close------------------------------------
}
return err;
}
做了些清除工作。
-----------------------------s_close-----------------------------------
LOGD("open called for devices %08x in mode %d...", devices, mode);
const char *stream = streamName(handle);
+++++++++++++++++++++++++++++streamName+++++++++++++++++++++++++++++++++++
const char *streamName(alsa_handle_t *handle)
{
return snd_pcm_stream_name(direction(handle));
+++++++++++++++++++++++++snd_pcm_stream_name+++++++++++++++++++++++++++++++++++++++
/**
* \brief get name of PCM stream type
* \param stream PCM stream type
* \return ascii name of PCM stream type
*/
const char *snd_pcm_stream_name(snd_pcm_stream_t stream)
{
if (stream > SND_PCM_STREAM_LAST)
return NULL;
return snd_pcm_stream_names[stream];
+++++++++++++++++++++++++++snd_pcm_stream_names+++++++++++++++++++++++++++++++++++++
static const char *const snd_pcm_stream_names[] = {
// #define STREAM(v) [SND_PCM_STREAM_##v] = #v
STREAM(PLAYBACK),
STREAM(CAPTURE),
};
---------------------------snd_pcm_stream_names-------------------------------------
}
-------------------------snd_pcm_stream_name---------------------------------------
}
-----------------------------streamName-----------------------------------
const char *devName = deviceName(handle, devices, mode, 1);
++++++++++++++++++++++++++deviceName++++++++++++++++++++++++++++++++++++++
//card_device =0, return the card name, card_device=1, return the card device name
const char *deviceName(alsa_handle_t *alsa_handle, uint32_t device, int mode, int card_device)
{
snd_ctl_t *handle;
int card, err, dev, idx;
snd_ctl_card_info_t *info;
snd_pcm_info_t *pcminfo;
snd_ctl_card_info_alloca(&info);
snd_pcm_info_alloca(&pcminfo);
int cardnum = 0;
char value[PROPERTY_VALUE_MAX];
snd_pcm_stream_t stream = direction(alsa_handle);
+++++++++++++++++++++++++++direction+++++++++++++++++++++++++++++++++++++
snd_pcm_stream_t direction(alsa_handle_t *handle)
{
return (handle->devices & AudioSystem::DEVICE_OUT_ALL) ? SND_PCM_STREAM_PLAYBACK
: SND_PCM_STREAM_CAPTURE;
}
---------------------------direction-------------------------------------
bool havespdifdevice = false;
bool havesgtldevice = false;
card = -1;
if (snd_card_next(&card) < 0 || card < 0) {
LOGD("no soundcards found...");
return "default";
}
++++++++++++++++++++++++++++snd_card_next++++++++++++++++++++++++++++++++++++
external\alsa-lib\src\control\Cards.c
/**
* \brief Try to determine the next card.
* \param rcard pointer to card number
* \result zero if success, otherwise a negative error code
*
* Tries to determine the next card from given card number.
* If card number is -1, then the first available card is
* returned. If the result card number is -1, no more cards
* are available.
*/
int snd_card_next(int *rcard)
{
int card;
if (rcard == NULL)
return -EINVAL;
card = *rcard;
card = card < 0 ? 0 : card + 1;
for (; card < 32; card++) {
if (snd_card_load(card)) {
+++++++++++++++++++++++++++snd_card_load+++++++++++++++++++++++++++++++++++++
/**
* \brief Try to load the driver for a card.
* \param card Card number.
* \return 1 if driver is present, zero if driver is not present
*/
int snd_card_load(int card)
{
return !!(snd_card_load1(card) >= 0);
+++++++++++++++++++++++++++++snd_card_load1+++++++++++++++++++++++++++++++++++
static int snd_card_load1(int card)
{
int res;
char control[sizeof(SND_FILE_CONTROL) + 10];
sprintf(control, SND_FILE_CONTROL, card);
res = snd_card_load2(control);
++++++++++++++++++++++++++++snd_card_load2++++++++++++++++++++++++++++++++++++
static int snd_card_load2(const char *control)
{
int open_dev;
snd_ctl_card_info_t info;
open_dev = snd_open_device(control, O_RDONLY);
+++++++++++++++++++++++++snd_open_device+++++++++++++++++++++++++++++++++++++++
static inline int snd_open_device(const char *filename, int fmode)
{
int fd;
#ifdef O_CLOEXEC
fmode |= O_CLOEXEC;
#endif
fd = open(filename, fmode);
/* open with resmgr */
#ifdef SUPPORT_RESMGR
if (fd < 0) {
if (errno == EAGAIN || errno == EBUSY)
return fd;
if (! access(filename, F_OK))
fd = rsm_open_device(filename, fmode);
}
#endif
if (fd >= 0)
fcntl(fd, F_SETFD, FD_CLOEXEC);
return fd;
}
-------------------------snd_open_device---------------------------------------
if (open_dev >= 0) {
if (ioctl(open_dev, SNDRV_CTL_IOCTL_CARD_INFO, &info) < 0) {
int err = -errno;
close(open_dev);
return err;
}
close(open_dev);
return info.card;
} else {
return -errno;
}
}
----------------------------snd_card_load2------------------------------------
#ifdef SUPPORT_ALOAD
if (res < 0) {
char aload[sizeof(SND_FILE_LOAD) + 10];
sprintf(aload, SND_FILE_LOAD, card);
res = snd_card_load2(aload);
}
#endif
return res;
}
-----------------------------snd_card_load1-----------------------------------
}
---------------------------snd_card_load-------------------------------------
*rcard = card;
return 0;
}
}
*rcard = -1;
return 0;
}
----------------------------snd_card_next------------------------------------
LOGD("**** List of %s Hardware Devices ****\n",
snd_pcm_stream_name(stream));
while (card >= 0) {
char name[32];
sprintf(name, "hw:%d", card);
if ((err = snd_ctl_open(&handle, name, 0)) < 0) {
LOGD("control open (%i): %s", card, snd_strerror(err));
goto next_card;
}
+++++++++++++++++++++++++++snd_ctl_open+++++++++++++++++++++++++++++++++++++
/**
* \brief Opens a CTL
* \param ctlp Returned CTL handle
* \param name ASCII identifier of the CTL handle
* \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC)
* \return 0 on success otherwise a negative error code
*/
int snd_ctl_open(snd_ctl_t **ctlp, const char *name, int mode)
{
int err;
assert(ctlp && name);
err = snd_config_update();
+++++++++++++++++++++++++++snd_config_update+++++++++++++++++++++++++++++++++++++
/**
* \brief Updates #snd_config by rereading the global configuration files (if needed).
* \return 0 if #snd_config was up to date, 1 if #snd_config was
* updated, otherwise a negative error code.
*
* \warning Whenever #snd_config is updated, all string pointers and
* configuration node handles previously obtained from it may become
* invalid.
*
* \par Errors:
* Any errors encountered when parsing the input or returned by hooks or
* functions.
*
* \par Conforming to:
* LSB 3.2
*/
int snd_config_update(void)
{
int err;
#ifdef HAVE_LIBPTHREAD
pthread_mutex_lock(&snd_config_update_mutex);
#endif
err = snd_config_update_r(&snd_config, &snd_config_global_update, NULL);
++++++++++++++++++++++snd_config_update_r++++++++++++++++++++++++++++++++++++++++++
/**
* \brief Updates a configuration tree by rereading the configuration files (if needed).
* \param[in,out] _top Address of the handle to the top-level node.
* \param[in,out] _update Address of a pointer to private update information.
* \param[in] cfgs A list of configuration file names, delimited with ':'.
* If \p cfgs is \c NULL, the default global
* configuration file is used.
* \return 0 if \a _top was up to date, 1 if the configuration files
* have been reread, otherwise a negative error code.
*
* The variables pointed to by \a _top and \a _update can be initialized
* to \c NULL before the first call to this function. The private
* update information holds information about all used configuration
* files that allows this function to detects changes to them; this data
* can be freed with #snd_config_update_free.
*
* The global configuration files are specified in the environment variable
* \c ALSA_CONFIG_PATH.
*
* \warning If the configuration tree is reread, all string pointers and
* configuration node handles previously obtained from this tree become
* invalid.
*
* \par Errors:
* Any errors encountered when parsing the input or returned by hooks or
* functions.
*/
int snd_config_update_r(snd_config_t **_top, snd_config_update_t **_update, const char *cfgs)
{
int err;
const char *configs, *c;
unsigned int k;
size_t l;
snd_config_update_t *local;
snd_config_update_t *update;
snd_config_t *top;
assert(_top && _update);
top = *_top;
update = *_update;
configs = cfgs;
if (!configs) {
/** The name of the environment variable containing the files list for #snd_config_update. */
// #define ALSA_CONFIG_PATH_VAR "ALSA_CONFIG_PATH"
configs = getenv(ALSA_CONFIG_PATH_VAR);
if (!configs || !*configs)
/** The name of the default files used by #snd_config_update. */
// #define ALSA_CONFIG_PATH_DEFAULT ALSA_CONFIG_DIR "/alsa.conf"
configs = ALSA_CONFIG_PATH_DEFAULT;
}
for (k = 0, c = configs; (l = strcspn(c, ": ")) > 0; ) {
c += l;
k++;
if (!*c)
break;
c++;
}
if (k == 0) {
local = NULL;
goto _reread;
}
local = (snd_config_update_t *)calloc(1, sizeof(snd_config_update_t));
if (!local)
return -ENOMEM;
local->count = k;
local->finfo = calloc(local->count, sizeof(struct finfo));
if (!local->finfo) {
free(local);
return -ENOMEM;
}
for (k = 0, c = configs; (l = strcspn(c, ": ")) > 0; ) {
char name[l + 1];
memcpy(name, c, l);
name[l] = 0;
err = snd_user_file(name, &local->finfo[k].name);
if (err < 0)
goto _end;
c += l;
k++;
if (!*c)
break;
c++;
}
for (k = 0; k < local->count; ++k) {
struct stat st;
struct finfo *lf = &local->finfo[k];
if (stat(lf->name, &st) >= 0) {
lf->dev = st.st_dev;
lf->ino = st.st_ino;
lf->mtime = st.st_mtime;
} else {
SNDERR("Cannot access file %s", lf->name);
free(lf->name);
memmove(&local->finfo[k], &local->finfo[k+1], sizeof(struct finfo) * (local->count - k - 1));
k--;
local->count--;
}
}
if (!update)
goto _reread;
if (local->count != update->count)
goto _reread;
for (k = 0; k < local->count; ++k) {
struct finfo *lf = &local->finfo[k];
struct finfo *uf = &update->finfo[k];
if (strcmp(lf->name, uf->name) != 0 ||
lf->dev != uf->dev ||
lf->ino != uf->ino ||
lf->mtime != uf->mtime)
goto _reread;
}
err = 0;
_end:
if (err < 0) {
if (top) {
snd_config_delete(top);
*_top = NULL;
}
if (update) {
snd_config_update_free(update);
*_update = NULL;
}
}
if (local)
snd_config_update_free(local);
return err;
_reread:
*_top = NULL;
*_update = NULL;
if (update) {
snd_config_update_free(update);
update = NULL;
}
if (top) {
snd_config_delete(top);
top = NULL;
}
err = snd_config_top(&top);
+++++++++++++++++++++++++++snd_config_top+++++++++++++++++++++++++++++++++++++
/**
* \brief Creates a top level configuration node.
* \param[out] config Handle to the new node.
* \return Zero if successful, otherwise a negative error code.
*
* The returned node is an empty compound node without a parent and
* without an id.
*
* \par Errors:
* <dl>
* <dt>-ENOMEM<dd>Out of memory.
* </dl>
*
* \par Conforming to:
* LSB 3.2
*/
int snd_config_top(snd_config_t **config)
{
assert(config);
return _snd_config_make(config, 0, SND_CONFIG_TYPE_COMPOUND);
+++++++++++++++++++++++++_snd_config_make+++++++++++++++++++++++++++++++++++++++
static int _snd_config_make(snd_config_t **config, char **id, snd_config_type_t type)
{
snd_config_t *n;
assert(config);
n = calloc(1, sizeof(*n));
if (n == NULL) {
if (*id) {
free(*id);
*id = NULL;
}
return -ENOMEM;
}
if (id) {
n->id = *id;
*id = NULL;
}
n->type = type;
if (type == SND_CONFIG_TYPE_COMPOUND)
INIT_LIST_HEAD(&n->u.compound.fields);
++++++++++++++++++++++++INIT_LIST_HEAD++++++++++++++++++++++++++++++++++++++++
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
------------------------INIT_LIST_HEAD----------------------------------------
*config = n;
return 0;
}
-------------------------_snd_config_make---------------------------------------
}
---------------------------snd_config_top-------------------------------------
if (err < 0)
goto _end;
if (!local)
goto _skip;
for (k = 0; k < local->count; ++k) {
snd_input_t *in;
err = snd_input_stdio_open(&in, local->finfo[k].name, "r");
++++++++++++++++++++++++snd_input_stdio_open++++++++++++++++++++++++++++++++++++++++
/**
* \brief Creates a new input object reading from a file.
* \param inputp The functions puts the pointer to the new input object
* at the address specified by \p inputp.
* \param file The name of the file to read from.
* \param mode The open mode, like \c fopen(3).
* \return Zero if successful, otherwise a negative error code.
*/
int snd_input_stdio_open(snd_input_t **inputp, const char *file, const char *mode)
{
int err;
FILE *fp = fopen(file, mode);
if (!fp) {
//SYSERR("fopen");
return -errno;
}
err = snd_input_stdio_attach(inputp, fp, 1);
+++++++++++++++++++++++++snd_input_stdio_attach+++++++++++++++++++++++++++++++++++++++
/**
* \brief Creates a new input object using an existing stdio \c FILE pointer.
* \param inputp The function puts the pointer to the new input object
* at the address specified by \p inputp.
* \param fp The \c FILE pointer to read from.
* Reading begins at the current file position.
* \param _close Close flag. Set this to 1 if #snd_input_close should close
* \p fp by calling \c fclose.
* \return Zero if successful, otherwise a negative error code.
*/
int snd_input_stdio_attach(snd_input_t **inputp, FILE *fp, int _close)
{
snd_input_t *input;
snd_input_stdio_t *stdio;
assert(inputp && fp);
stdio = calloc(1, sizeof(*stdio));
if (!stdio)
return -ENOMEM;
input = calloc(1, sizeof(*input));
if (!input) {
free(stdio);
return -ENOMEM;
}
stdio->fp = fp;
stdio->close = _close;
input->type = SND_INPUT_STDIO;
input->ops = &snd_input_stdio_ops;
input->private_data = stdio;
*inputp = input;
return 0;
}
-------------------------snd_input_stdio_attach---------------------------------------
if (err < 0)
fclose(fp);
return err;
}
------------------------snd_input_stdio_open----------------------------------------
if (err >= 0) {
err = snd_config_load(top, in);
+++++++++++++++++++++++snd_config_load+++++++++++++++++++++++++++++++++++++++++
/**
* \brief Loads a configuration tree.
* \param config Handle to a top level configuration node.
* \param in Input handle to read the configuration from.
* \return Zero if successful, otherwise a negative error code.
*
* The definitions loaded from the input are added to \a config, which
* must be a compound node.
*
* \par Errors:
* Any errors encountered when parsing the input or returned by hooks or
* functions.
*
* \par Conforming to:
* LSB 3.2
*/
int snd_config_load(snd_config_t *config, snd_input_t *in)
{
return snd_config_load1(config, in, 0);
+++++++++++++++++++++++++++snd_config_load1+++++++++++++++++++++++++++++++++++++
static int snd_config_load1(snd_config_t *config, snd_input_t *in, int override)
{
int err;
input_t input;
struct filedesc *fd, *fd_next;
assert(config && in);
fd = malloc(sizeof(*fd));
if (!fd)
return -ENOMEM;
fd->name = NULL;
fd->in = in;
fd->line = 1;
fd->column = 0;
fd->next = NULL;
input.current = fd;
input.unget = 0;
err = parse_defs(config, &input, 0, override);
fd = input.current;
if (err < 0) {
const char *str;
switch (err) {
case LOCAL_UNTERMINATED_STRING:
str = "Unterminated string";
err = -EINVAL;
break;
case LOCAL_UNTERMINATED_QUOTE:
str = "Unterminated quote";
err = -EINVAL;
break;
case LOCAL_UNEXPECTED_CHAR:
str = "Unexpected char";
err = -EINVAL;
break;
case LOCAL_UNEXPECTED_EOF:
str = "Unexpected end of file";
err = -EINVAL;
break;
default:
str = strerror(-err);
break;
}
SNDERR("%s:%d:%d:%s", fd->name ? fd->name : "_toplevel_", fd->line, fd->column, str);
goto _end;
}
if (get_char(&input) != LOCAL_UNEXPECTED_EOF) {
SNDERR("%s:%d:%d:Unexpected }", fd->name ? fd->name : "", fd->line, fd->column);
err = -EINVAL;
goto _end;
}
_end:
while (fd->next) {
fd_next = fd->next;
snd_input_close(fd->in);
free(fd->name);
free(fd);
fd = fd_next;
}
free(fd);
return err;
}
---------------------------snd_config_load1-------------------------------------
}
-----------------------snd_config_load-----------------------------------------
snd_input_close(in);
if (err < 0) {
SNDERR("%s may be old or corrupted: consider to remove or fix it", local->finfo[k].name);
goto _end;
}
} else {
SNDERR("cannot access file %s", local->finfo[k].name);
}
}
_skip:
err = snd_config_hooks(top, NULL);
if (err < 0) {
SNDERR("hooks failed, removing configuration");
goto _end;
}
*_top = top;
*_update = local;
return 1;
}
----------------------snd_config_update_r------------------------------------------
#ifdef HAVE_LIBPTHREAD
pthread_mutex_unlock(&snd_config_update_mutex);
#endif
return err;
}
---------------------------snd_config_update-------------------------------------
if (err < 0)
return err;
return snd_ctl_open_noupdate(ctlp, snd_config, name, mode);
++++++++++++++++++++++++++++snd_ctl_open_noupdate++++++++++++++++++++++++++++++++++++
static int snd_ctl_open_noupdate(snd_ctl_t **ctlp, snd_config_t *root, const char *name, int mode)
{
int err;
snd_config_t *ctl_conf;
// 这儿,只看看snd_config_search_definition的注释吧
err = snd_config_search_definition(root, "ctl", name, &ctl_conf);
+++++++++++++++++++++++snd_config_search_definition+++++++++++++++++++++++++++++++++++++++++
/**
* \brief Searches for a definition in a configuration tree, using
* aliases and expanding hooks and arguments.
* \param[in] config Handle to the configuration (sub)tree to search.
* \param[in] base Implicit key base, or \c NULL for none.
* \param[in] name Key suffix, optionally with arguments.
* \param[out] result The function puts the handle to the expanded found
* node at the address specified by \a result.
* \return A non-negative value if successful, otherwise a negative error code.
*
* This functions searches for a child node of \a config, allowing
* aliases and expanding hooks, like #snd_config_search_alias_hooks.
*
* If \a name contains a colon (:), the rest of the string after the
* colon contains arguments that are expanded as with
* #snd_config_expand.
*
* In any case, \a result is a new node that must be freed by the
* caller.
*
* \par Errors:
* <dl>
* <dt>-ENOENT<dd>An id in \a key or an alias id does not exist.
* <dt>-ENOENT<dd>\a config or one of its child nodes to be searched is
* not a compound node.
* </dl>
* Additionally, any errors encountered when parsing the hook
* definitions or arguments, or returned by (hook) functions.
*/
-----------------------snd_config_search_definition-----------------------------------------
if (err < 0) {
SNDERR("Invalid CTL %s", name);
return err;
}
err = snd_ctl_open_conf(ctlp, name, root, ctl_conf, mode);
++++++++++++++++++++++snd_ctl_open_conf++++++++++++++++++++++++++++++++++++++++++
static int snd_ctl_open_conf(snd_ctl_t **ctlp, const char *name,
snd_config_t *ctl_root, snd_config_t *ctl_conf, int mode)
{
const char *str;
char *buf = NULL, *buf1 = NULL;
int err;
snd_config_t *conf, *type_conf = NULL;
snd_config_iterator_t i, next;
const char *lib = NULL, *open_name = NULL;
const char *id;
int (*open_func)(snd_ctl_t **, const char *, snd_config_t *, snd_config_t *, int) = NULL;
#ifndef PIC
extern void *snd_control_open_symbols(void);
#endif
void *h = NULL;
if (snd_config_get_type(ctl_conf) != SND_CONFIG_TYPE_COMPOUND) {
if (name)
SNDERR("Invalid type for CTL %s definition", name);
else
SNDERR("Invalid type for CTL definition");
return -EINVAL;
}
err = snd_config_search(ctl_conf, "type", &conf);
if (err < 0) {
SNDERR("type is not defined");
return err;
}
err = snd_config_get_id(conf, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(ctl_root, "ctl_type", str, &type_conf);
if (err >= 0) {
if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) {
SNDERR("Invalid type for CTL type %s definition", str);
goto _err;
}
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {
err = snd_config_get_string(n, &lib);
if (err < 0) {
SNDERR("Invalid type for %s", id);
goto _err;
}
continue;
}
if (strcmp(id, "open") == 0) {
err = snd_config_get_string(n, &open_name);
if (err < 0) {
SNDERR("Invalid type for %s", id);
goto _err;
}
continue;
}
SNDERR("Unknown field %s", id);
err = -EINVAL;
goto _err;
}
}
if (!open_name) {
buf = malloc(strlen(str) + 32);
if (buf == NULL) {
err = -ENOMEM;
goto _err;
}
open_name = buf;
sprintf(buf, "_snd_ctl_%s_open", str);
}
if (!lib) {
const char *const *build_in = build_in_ctls;
while (*build_in) {
if (!strcmp(*build_in, str))
break;
build_in++;
}
if (*build_in == NULL) {
buf1 = malloc(strlen(str) + sizeof(ALSA_PLUGIN_DIR) + 32);
if (buf1 == NULL) {
err = -ENOMEM;
goto _err;
}
lib = buf1;
sprintf(buf1, "%s/libasound_module_ctl_%s.so", ALSA_PLUGIN_DIR, str);
}
}
#ifndef PIC
snd_control_open_symbols();
#endif
open_func = snd_dlobj_cache_lookup(open_name);
if (open_func) {
err = 0;
goto _err;
}
+++++++++++++++++++++++snd_dlobj_cache_lookup+++++++++++++++++++++++++++++++++++++++++
void *snd_dlobj_cache_lookup(const char *name)
{
struct list_head *p;
struct dlobj_cache *c;
list_for_each(p, &pcm_dlobj_list) {
c = list_entry(p, struct dlobj_cache, list);
if (strcmp(c->name, name) == 0)
return c->func;
}
return NULL;
}
往pcm_dlobj_list中添加对象的地方:
++++++++++++++++++++++++++++++snd_dlobj_cache_add++++++++++++++++++++++++++++++++++
int snd_dlobj_cache_add(const char *name, void *dlobj, void *open_func)
{
struct list_head *p;
struct dlobj_cache *c;
list_for_each(p, &pcm_dlobj_list) {
c = list_entry(p, struct dlobj_cache, list);
if (strcmp(c->name, name) == 0)
return 0; /* already exists */
}
c = malloc(sizeof(*c));
if (! c)
return -ENOMEM;
c->name = strdup(name);
if (! c->name) {
free(c);
return -ENOMEM;
}
c->obj = dlobj;
c->func = open_func;
list_add_tail(&c->list, &pcm_dlobj_list);
return 0;
}
------------------------------snd_dlobj_cache_add----------------------------------
函数snd_ctl_open_conf中调用了函数snd_dlobj_cache_add。
-----------------------snd_dlobj_cache_lookup-----------------------------------------
h = snd_dlopen(lib, RTLD_NOW);
if (h)
open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_CONTROL_DLSYM_VERSION));
+++++++++++++++++++++snd_dlsym+++++++++++++++++++++++++++++++++++++++++++
函数snd_dlsym的注释:
/**
* \brief Resolves a symbol from a dynamic library - ALSA wrapper for \c dlsym.
* \param handle Library handle, similar to \c dlsym.
* \param name Symbol name.
* \param version Version of the symbol.
*
* This function can emulate dynamic linking for the static build of
* the alsa-lib library.
*
* This special version of the \c dlsym function checks also the version
* of the symbol. A versioned symbol should be defined using the
* #SND_DLSYM_BUILD_VERSION macro.
*/
---------------------snd_dlsym-------------------------------------------
err = 0;
if (!h) {
SNDERR("Cannot open shared library %s", lib);
err = -ENOENT;
} else if (!open_func) {
SNDERR("symbol %s is not defined inside %s", open_name, lib);
snd_dlclose(h);
err = -ENXIO;
}
_err:
if (type_conf)
snd_config_delete(type_conf);
if (err >= 0) {
err = open_func(ctlp, name, ctl_root, ctl_conf, mode);
if (err >= 0) {
if (h /*&& (mode & SND_CTL_KEEP_ALIVE)*/) {
snd_dlobj_cache_add(open_name, h, open_func);
h = NULL;
}
(*ctlp)->dl_handle = h;
err = 0;
} else {
if (h)
snd_dlclose(h);
}
}
free(buf);
free(buf1);
return err;
}
----------------------snd_ctl_open_conf------------------------------------------
snd_config_delete(ctl_conf);
return err;
}
----------------------------snd_ctl_open_noupdate------------------------------------
}
---------------------------snd_ctl_open-------------------------------------
if ((err = snd_ctl_card_info(handle, info)) < 0) {
LOGD("control hardware info (%i): %s", card, snd_strerror(err));
snd_ctl_close(handle);
goto next_card;
}
+++++++++++++++++++++++++snd_ctl_card_info+++++++++++++++++++++++++++++++++++++++
/**
* \brief Get card related information
* \param ctl CTL handle
* \param info Card info pointer
* \return 0 on success otherwise a negative error code
*/
int snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info)
{
assert(ctl && info);
return ctl->ops->card_info(ctl, info);
}
-------------------------snd_ctl_card_info---------------------------------------
dev = -1;
while (1) {
unsigned int count;
if (snd_ctl_pcm_next_device(handle, &dev)<0)
LOGD("snd_ctl_pcm_next_device");
if (dev < 0)
break;
snd_pcm_info_set_device(pcminfo, dev);
snd_pcm_info_set_subdevice(pcminfo, 0);
snd_pcm_info_set_stream(pcminfo, stream);
if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
if (err != -ENOENT)
LOGD("control digital audio info (%i): %s", card, snd_strerror(err));
continue;
}
LOGD("card %i: %s [%s], device %i: %s [%s]\n",
card, snd_ctl_card_info_get_id(info), snd_ctl_card_info_get_name(info),
dev,
snd_pcm_info_get_id(pcminfo),
snd_pcm_info_get_name(pcminfo));
if(strcmp(snd_pcm_info_get_id(pcminfo),"IMX SPDIF mxc spdif-0")==0) {
if(card_device==0) sprintf(spdifcardname, "hw:0%d", card);
else
sprintf(spdifcardname, "hw:%d,%d", card, dev);
havespdifdevice = true;
}
if(strcmp(snd_pcm_info_get_id(pcminfo),"SGTL5000 SGTL5000-0")==0) {
if(card_device==0) sprintf(sgtlcardname, "hw:0%d", card);
else sprintf(sgtlcardname, "hw:%d,%d", card, dev);
havesgtldevice = true;
}
cardnum++;
}
snd_ctl_close(handle);
next_card:
if (snd_card_next(&card) < 0) {
LOGD("snd_card_next");
break;
}
}
property_get("ro.HDMI_AUDIO_OUTPUT", value, "");
if((device & AudioSystem::DEVICE_OUT_WIRED_HDMI) && havespdifdevice && (strcmp(value, "1") == 0))
{
return spdifcardname;
}else if(havesgtldevice)
{
return sgtlcardname;
}
return "default";
}
--------------------------deviceName--------------------------------------
// The PCM stream is opened in blocking mode, per ALSA defaults. The
// AudioFlinger seems to assume blocking mode too, so asynchronous mode
// should not be used.
int err = snd_pcm_open(&handle->handle, devName, direction(handle), 0);
++++++++++++++++++++++++snd_pcm_open++++++++++++++++++++++++++++++++++++++++\
想到了前面的函数snd_ctl_open,呜呼!危乎多哉!
/**
* \brief Opens a PCM
* \param pcmp Returned PCM handle
* \param name ASCII identifier of the PCM handle
* \param stream Wanted stream
* \param mode Open mode (see #SND_PCM_NONBLOCK, #SND_PCM_ASYNC)
* \return 0 on success otherwise a negative error code
*/
int snd_pcm_open(snd_pcm_t **pcmp, const char *name,
snd_pcm_stream_t stream, int mode)
{
int err;
assert(pcmp && name);
// 这个函数已经看过了
err = snd_config_update();
if (err < 0)
return err;
return snd_pcm_open_noupdate(pcmp, snd_config, name, stream, mode, 0);
+++++++++++++++++++++++++++snd_pcm_open_noupdate+++++++++++++++++++++++++++++++++++++
static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root,
const char *name, snd_pcm_stream_t stream,
int mode, int hop)
{
int err;
snd_config_t *pcm_conf;
const char *str;
err = snd_config_search_definition(root, "pcm", name, &pcm_conf);
if (err < 0) {
SNDERR("Unknown PCM %s", name);
return err;
}
if (snd_config_get_string(pcm_conf, &str) >= 0)
err = snd_pcm_open_noupdate(pcmp, root, str, stream, mode,
hop + 1);
else {
snd_config_set_hop(pcm_conf, hop);
++++++++++++++++++++++++snd_config_set_hop++++++++++++++++++++++++++++++++++++++++
void snd_config_set_hop(snd_config_t *conf, int hop)
{
conf->hop = hop;
}
------------------------snd_config_set_hop----------------------------------------
err = snd_pcm_open_conf(pcmp, name, root, pcm_conf, stream, mode);
++++++++++++++++++++++++++++++snd_pcm_open_conf++++++++++++++++++++++++++++++++++
想起来一个与其非常相似的函数:snd_ctl_open_conf。
调用的函数上面都有介绍过,此处就不再说了。
static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name,
snd_config_t *pcm_root, snd_config_t *pcm_conf,
snd_pcm_stream_t stream, int mode)
{
const char *str;
char *buf = NULL, *buf1 = NULL;
int err;
snd_config_t *conf, *type_conf = NULL;
snd_config_iterator_t i, next;
const char *id;
const char *lib = NULL, *open_name = NULL;
int (*open_func)(snd_pcm_t **, const char *,
snd_config_t *, snd_config_t *,
snd_pcm_stream_t, int) = NULL;
#ifndef PIC
extern void *snd_pcm_open_symbols(void);
#endif
void *h = NULL;
if (snd_config_get_type(pcm_conf) != SND_CONFIG_TYPE_COMPOUND) {
char *val;
id = NULL;
snd_config_get_id(pcm_conf, &id);
val = NULL;
snd_config_get_ascii(pcm_conf, &val);
SNDERR("Invalid type for PCM %s%sdefinition (id: %s, value: %s)", name ? name : "", name ? " " : "", id, val);
free(val);
return -EINVAL;
}
err = snd_config_search(pcm_conf, "type", &conf);
if (err < 0) {
SNDERR("type is not defined");
return err;
}
err = snd_config_get_id(conf, &id);
if (err < 0) {
SNDERR("unable to get id");
return err;
}
err = snd_config_get_string(conf, &str);
if (err < 0) {
SNDERR("Invalid type for %s", id);
return err;
}
err = snd_config_search_definition(pcm_root, "pcm_type", str, &type_conf);
if (err >= 0) {
if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) {
SNDERR("Invalid type for PCM type %s definition", str);
goto _err;
}
snd_config_for_each(i, next, type_conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0)
continue;
if (strcmp(id, "lib") == 0) {
err = snd_config_get_string(n, &lib);
if (err < 0) {
SNDERR("Invalid type for %s", id);
goto _err;
}
continue;
}
if (strcmp(id, "open") == 0) {
err = snd_config_get_string(n, &open_name);
if (err < 0) {
SNDERR("Invalid type for %s", id);
goto _err;
}
continue;
}
SNDERR("Unknown field %s", id);
err = -EINVAL;
goto _err;
}
}
if (!open_name) {
buf = malloc(strlen(str) + 32);
if (buf == NULL) {
err = -ENOMEM;
goto _err;
}
open_name = buf;
sprintf(buf, "_snd_pcm_%s_open", str);
}
if (!lib) {
const char *const *build_in = build_in_pcms;
while (*build_in) {
if (!strcmp(*build_in, str))
break;
build_in++;
}
if (*build_in == NULL) {
buf1 = malloc(strlen(str) + sizeof(ALSA_PLUGIN_DIR) + 32);
if (buf1 == NULL) {
err = -ENOMEM;
goto _err;
}
lib = buf1;
sprintf(buf1, "%s/libasound_module_pcm_%s.so", ALSA_PLUGIN_DIR, str);
}
}
#ifndef PIC
snd_pcm_open_symbols();
/* this call is for static linking only */
#endif
open_func = snd_dlobj_cache_lookup(open_name);
if (open_func) {
err = 0;
goto _err;
}
h = snd_dlopen(lib, RTLD_NOW);
if (h)
open_func = snd_dlsym(h, open_name, SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION));
err = 0;
if (!h) {
SNDERR("Cannot open shared library %s",
lib ? lib : "[builtin]");
err = -ENOENT;
} else if (!open_func) {
SNDERR("symbol %s is not defined inside %s", open_name,
lib ? lib : "[builtin]");
snd_dlclose(h);
err = -ENXIO;
}
_err:
if (err >= 0) {
err = open_func(pcmp, name, pcm_root, pcm_conf, stream, mode);
if (err >= 0) {
if (h /*&& (mode & SND_PCM_KEEP_ALIVE)*/) {
snd_dlobj_cache_add(open_name, h, open_func);
h = NULL;
}
(*pcmp)->dl_handle = h;
err = 0;
} else {
if (h)
snd_dlclose(h);
}
}
if (type_conf)
snd_config_delete(type_conf);
free(buf);
free(buf1);
return err;
}
------------------------------snd_pcm_open_conf----------------------------------
}
snd_config_delete(pcm_conf);
return err;
}
---------------------------snd_pcm_open_noupdate-------------------------------------
}
------------------------snd_pcm_open----------------------------------------
if (err < 0) {
LOGE("Failed to Initialize any ALSA %s device: %s", stream, strerror(err));
return NO_INIT;
}
err = setHardwareParams(handle);
if (err == NO_ERROR) err = setSoftwareParams(handle);
LOGI("Initialized ALSA %s device %s", stream, devName);
handle->curDev = devices;
handle->curMode = mode;
return err;
}
-----------------------------s_open-----------------------------------
if (err) break;
if (devices & AudioSystem::DEVICE_OUT_WIRED_HDMI){
strcpy(mCurCard ,SPDIF);
mMixer = mMixerSpdif;
} else {
strcpy(mCurCard,SGTL5000);
mMixer = mMixerSgtl5000;
}
out = new AudioStreamOutALSA(this, &(*it));
err = out->set(format, channels, sampleRate);
break;
}
----------------------------------------------------------------
至此,it的来历基本上清楚了。
下面看看函数snd_pcm_mmap_writei和snd_pcm_writei的实现。
回忆一下上面的代码:
if (mHandle->mmap)
n = snd_pcm_mmap_writei(mHandle->handle,
(char *)buffer + sent,
snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent));
else
n = snd_pcm_writei(mHandle->handle,
(char *)buffer + sent,
snd_pcm_bytes_to_frames(mHandle->handle, bytes - sent));
+++++++++++++++++++++++++snd_pcm_mmap_writei+++++++++++++++++++++++++++++++++++++++
/**
* \brief Write interleaved frames to a PCM using direct buffer (mmap)
* \param pcm PCM handle
* \param buffer frames containing buffer
* \param size frames to be written
* \return a positive number of frames actually written otherwise a
* negative error code
* \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING)
* \retval -EPIPE an underrun occurred
* \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)
*
* If the blocking behaviour is selected, then routine waits until
* all requested bytes are played or put to the playback ring buffer.
* The count of bytes can be less only if a signal or underrun occurred.
*
* If the non-blocking behaviour is selected, then routine doesn't wait at all.
*/
snd_pcm_sframes_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
snd_pcm_channel_area_t areas[pcm->channels];
snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
return snd_pcm_write_areas(pcm, areas, 0, size,
snd_pcm_mmap_write_areas);
++++++++++++++++++++++++++++snd_pcm_write_areas++++++++++++++++++++++++++++++++++++
snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
snd_pcm_uframes_t offset, snd_pcm_uframes_t size,
snd_pcm_xfer_areas_func_t func)
{
snd_pcm_uframes_t xfer = 0;
snd_pcm_sframes_t err = 0;
snd_pcm_state_t state;
if (size == 0)
return 0;
while (size > 0) {
snd_pcm_uframes_t frames;
snd_pcm_sframes_t avail;
_again:
state = snd_pcm_state(pcm);
++++++++++++++++++++++++++++++snd_pcm_state++++++++++++++++++++++++++++++++++
/**
* \brief Return PCM state
* \param pcm PCM handle
* \return PCM state #snd_pcm_state_t of given PCM handle
*
* This is a faster way to obtain only the PCM state without calling
* \link ::snd_pcm_status() \endlink.
*/
snd_pcm_state_t snd_pcm_state(snd_pcm_t *pcm)
{
assert(pcm);
return pcm->fast_ops->state(pcm->fast_op_arg);
}
------------------------------snd_pcm_state----------------------------------
switch (state) {
case SND_PCM_STATE_PREPARED:
case SND_PCM_STATE_PAUSED:
break;
case SND_PCM_STATE_RUNNING:
err = snd_pcm_hwsync(pcm);
if (err < 0)
goto _end;
+++++++++++++++++++++++++++snd_pcm_hwsync+++++++++++++++++++++++++++++++++++++
/**
* \brief (DEPRECATED) Synchronize stream position with hardware
* \param pcm PCM handle
* \return 0 on success otherwise a negative error code
*
* Note this function does not update the actual r/w pointer
* for applications. The function #snd_pcm_avail_update()
* have to be called before any mmap begin+commit operation.
*/
int snd_pcm_hwsync(snd_pcm_t *pcm)
{
assert(pcm);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
return pcm->fast_ops->hwsync(pcm->fast_op_arg);
}
---------------------------snd_pcm_hwsync-------------------------------------
break;
case SND_PCM_STATE_XRUN:
err = -EPIPE;
goto _end;
case SND_PCM_STATE_SUSPENDED:
err = -ESTRPIPE;
goto _end;
case SND_PCM_STATE_DISCONNECTED:
err = -ENODEV;
goto _end;
default:
err = -EBADFD;
goto _end;
}
avail = snd_pcm_avail_update(pcm);
if (avail < 0) {
err = avail;
goto _end;
}
+++++++++++++++++++++++++++++snd_pcm_avail_update+++++++++++++++++++++++++++++++++++
/**
* \brief Return number of frames ready to be read (capture) / written (playback)
* \param pcm PCM handle
* \return a positive number of frames ready otherwise a negative
* error code
*
* On capture does all the actions needed to transport to application
* level all the ready frames across underlying layers.
*
* The position is not synced with hardware (driver) position in the sound
* ring buffer in this function. This function is a light version of
* #snd_pcm_avail() .
*
* Using this function is ideal after poll() or select() when audio
* file descriptor made the event and when application expects just period
* timing.
*
* Also this function might be called after #snd_pcm_delay() or
* #snd_pcm_hwsync() functions to move private ring buffer pointers
* in alsa-lib (the internal plugin chain).
*/
snd_pcm_sframes_t snd_pcm_avail_update(snd_pcm_t *pcm)
{
return pcm->fast_ops->avail_update(pcm->fast_op_arg);
}
-----------------------------snd_pcm_avail_update-----------------------------------
if ((state == SND_PCM_STATE_RUNNING &&
(snd_pcm_uframes_t)avail < pcm->avail_min &&
size > (snd_pcm_uframes_t)avail)) {
if (pcm->mode & SND_PCM_NONBLOCK) {
err = -EAGAIN;
goto _end;
}
err = snd_pcm_wait(pcm, -1);
if (err < 0)
break;
goto _again;
+++++++++++++++++++++++++++snd_pcm_wait+++++++++++++++++++++++++++++++++++++
/**
* \brief Wait for a PCM to become ready
* \param pcm PCM handle
* \param timeout maximum time in milliseconds to wait,
* a negative value means infinity
* \return a positive value on success otherwise a negative error code
* (-EPIPE for the xrun and -ESTRPIPE for the suspended status,
* others for general errors)
* \retval 0 timeout occurred
* \retval 1 PCM stream is ready for I/O
*/
int snd_pcm_wait(snd_pcm_t *pcm, int timeout)
{
if (snd_pcm_mmap_avail(pcm) >= pcm->avail_min) {
/* check more precisely */
switch (snd_pcm_state(pcm)) {
case SND_PCM_STATE_XRUN:
return -EPIPE;
case SND_PCM_STATE_SUSPENDED:
return -ESTRPIPE;
case SND_PCM_STATE_DISCONNECTED:
return -ENODEV;
default:
return 1;
}
}
+++++++++++++++++++++++++snd_pcm_mmap_avail+++++++++++++++++++++++++++++++++++++++
static inline snd_pcm_uframes_t snd_pcm_mmap_avail(snd_pcm_t *pcm)
{
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
// 先只看播放的
return snd_pcm_mmap_playback_avail(pcm);
++++++++++++++++++++++++++++snd_pcm_mmap_playback_avail++++++++++++++++++++++++++++++++++++
static inline snd_pcm_uframes_t snd_pcm_mmap_playback_avail(snd_pcm_t *pcm)
{
snd_pcm_sframes_t avail;
avail = *pcm->hw.ptr + pcm->buffer_size - *pcm->appl.ptr;
if (avail < 0)
avail += pcm->boundary;
else if ((snd_pcm_uframes_t) avail >= pcm->boundary)
avail -= pcm->boundary;
return avail;
}
----------------------------snd_pcm_mmap_playback_avail------------------------------------
else
return snd_pcm_mmap_capture_avail(pcm);
}
--------------------------snd_pcm_mmap_avail--------------------------------------
return snd_pcm_wait_nocheck(pcm, timeout);
}
----------------------------snd_pcm_wait------------------------------------
}
frames = size;
if (frames > (snd_pcm_uframes_t) avail)
frames = avail;
if (! frames)
break;
// func是传入的参数snd_pcm_mmap_write_areas
err = func(pcm, areas, offset, frames);
+++++++++++++++++++++++++++func是传入的参数snd_pcm_mmap_write_areas+++++++++++++++++++++++++++++++++++++
static snd_pcm_sframes_t snd_pcm_mmap_write_areas(snd_pcm_t *pcm,
const snd_pcm_channel_area_t *areas,
snd_pcm_uframes_t offset,
snd_pcm_uframes_t size)
{
snd_pcm_uframes_t xfer = 0;
if (snd_pcm_mmap_playback_avail(pcm) < size) {
SNDMSG("too short avail %ld to size %ld", snd_pcm_mmap_playback_avail(pcm), size);
return -EPIPE;
}
while (size > 0) {
const snd_pcm_channel_area_t *pcm_areas;
snd_pcm_uframes_t pcm_offset;
snd_pcm_uframes_t frames = size;
snd_pcm_sframes_t result;
snd_pcm_mmap_begin(pcm, &pcm_areas, &pcm_offset, &frames);
+++++++++++++++++++++++++snd_pcm_mmap_begin+++++++++++++++++++++++++++++++++++++++
/**
* \brief Application request to access a portion of direct (mmap) area
* \param pcm PCM handle
* \param areas Returned mmap channel areas
* \param offset Returned mmap area offset in area steps (== frames)
* \param frames mmap area portion size in frames (wanted on entry, contiguous available on exit)
* \return 0 on success otherwise a negative error code
*
* It is necessary to call the snd_pcm_avail_update() function directly before
* this call. Otherwise, this function can return a wrong count of available frames.
*
* The function should be called before a sample-direct area can be accessed.
* The resulting size parameter is always less or equal to the input count of frames
* and can be zero, if no frames can be processed (the ring buffer is full).
*
* See the snd_pcm_mmap_commit() function to finish the frame processing in
* the direct areas.
*/
int snd_pcm_mmap_begin(snd_pcm_t *pcm,
const snd_pcm_channel_area_t **areas,
snd_pcm_uframes_t *offset,
snd_pcm_uframes_t *frames)
{
snd_pcm_uframes_t cont;
snd_pcm_uframes_t f;
snd_pcm_uframes_t avail;
const snd_pcm_channel_area_t *xareas;
assert(pcm && areas && offset && frames);
xareas = snd_pcm_mmap_areas(pcm);
if (xareas == NULL)
return -EBADFD;
+++++++++++++++++++++++++++snd_pcm_mmap_areas+++++++++++++++++++++++++++++++++++++
static inline const snd_pcm_channel_area_t *snd_pcm_mmap_areas(snd_pcm_t *pcm)
{
if (pcm->stopped_areas &&
snd_pcm_state(pcm) != SND_PCM_STATE_RUNNING)
return pcm->stopped_areas;
return pcm->running_areas;
}
---------------------------snd_pcm_mmap_areas-------------------------------------
*areas = xareas;
*offset = *pcm->appl.ptr % pcm->buffer_size;
avail = snd_pcm_mmap_avail(pcm);
if (avail > pcm->buffer_size)
avail = pcm->buffer_size;
cont = pcm->buffer_size - *offset;
f = *frames;
if (f > avail)
f = avail;
if (f > cont)
f = cont;
*frames = f;
return 0;
}
-------------------------snd_pcm_mmap_begin---------------------------------------
// 把数据从areas copy 到 pcm_areas
// areas是传入的参数
// pcm_areas是pcm(snd_pcm_t)的stopped_areas或者running_areas。
snd_pcm_areas_copy(pcm_areas, pcm_offset,
areas, offset,
pcm->channels,
frames, pcm->format);
+++++++++++++++++++++++++snd_pcm_areas_copy+++++++++++++++++++++++++++++++++++++++
/**
* \brief Copy one or more areas
* \param dst_areas destination areas specification (one for each channel)
* \param dst_offset offset in frames inside destination area
* \param src_areas source areas specification (one for each channel)
* \param src_offset offset in frames inside source area
* \param channels channels count
* \param frames frames to copy
* \param format PCM sample format
* \return 0 on success otherwise a negative error code
*/
int snd_pcm_areas_copy(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
unsigned int channels, snd_pcm_uframes_t frames, snd_pcm_format_t format)
{
int width = snd_pcm_format_physical_width(format);
assert(dst_areas);
assert(src_areas);
if (! channels) {
SNDMSG("invalid channels %d", channels);
return -EINVAL;
}
if (! frames) {
SNDMSG("invalid frames %ld", frames);
return -EINVAL;
}
while (channels > 0) {
unsigned int step = src_areas->step;
void *src_addr = src_areas->addr;
const snd_pcm_channel_area_t *src_start = src_areas;
void *dst_addr = dst_areas->addr;
const snd_pcm_channel_area_t *dst_start = dst_areas;
int channels1 = channels;
unsigned int chns = 0;
while (dst_areas->step == step) {
channels1--;
chns++;
src_areas++;
dst_areas++;
if (channels1 == 0 ||
src_areas->step != step ||
src_areas->addr != src_addr ||
dst_areas->addr != dst_addr ||
src_areas->first != src_areas[-1].first + width ||
dst_areas->first != dst_areas[-1].first + width)
break;
}
if (chns > 1 && chns * width == step) {
/* Collapse the areas */
snd_pcm_channel_area_t s, d;
s.addr = src_start->addr;
s.first = src_start->first;
s.step = width;
d.addr = dst_start->addr;
d.first = dst_start->first;
d.step = width;
snd_pcm_area_copy(&d, dst_offset * chns,
&s, src_offset * chns,
frames * chns, format);
channels -= chns;
++++++++++++++++++++++++++++snd_pcm_area_copy++++++++++++++++++++++++++++++++++++
/**
* \brief Copy an area
* \param dst_area destination area specification
* \param dst_offset offset in frames inside destination area
* \param src_area source area specification
* \param src_offset offset in frames inside source area
* \param samples samples to copy
* \param format PCM sample format
* \return 0 on success otherwise a negative error code
*/
int snd_pcm_area_copy(const snd_pcm_channel_area_t *dst_area, snd_pcm_uframes_t dst_offset,
const snd_pcm_channel_area_t *src_area, snd_pcm_uframes_t src_offset,
unsigned int samples, snd_pcm_format_t format)
{
/* FIXME: sub byte resolution and odd dst_offset */
const char *src;
char *dst;
int width;
int src_step, dst_step;
if (dst_area == src_area && dst_offset == src_offset)
return 0;
if (!src_area->addr)
return snd_pcm_area_silence(dst_area, dst_offset, samples, format);
src = snd_pcm_channel_area_addr(src_area, src_offset);
if (!dst_area->addr)
return 0;
+++++++++++++++++++++++snd_pcm_channel_area_addr+++++++++++++++++++++++++++++++++++++++++
static inline void *snd_pcm_channel_area_addr(const snd_pcm_channel_area_t *area, snd_pcm_uframes_t offset)
{
unsigned int bitofs = area->first + area->step * offset;
assert(bitofs % 8 == 0);
return (char *) area->addr + bitofs / 8;
}
-----------------------snd_pcm_channel_area_addr-----------------------------------------
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
width = snd_pcm_format_physical_width(format);
if (src_area->step == (unsigned int) width &&
dst_area->step == (unsigned int) width) {
size_t bytes = samples * width / 8;
samples -= bytes * 8 / width;
memcpy(dst, src, bytes);
if (samples == 0)
return 0;
}
src_step = src_area->step / 8;
dst_step = dst_area->step / 8;
switch (width) {
case 4: {
int srcbit = src_area->first % 8;
int srcbit_step = src_area->step % 8;
int dstbit = dst_area->first % 8;
int dstbit_step = dst_area->step % 8;
while (samples-- > 0) {
unsigned char srcval;
if (srcbit)
srcval = *src & 0x0f;
else
srcval = *src & 0xf0;
if (dstbit)
*dst &= 0xf0;
else
*dst &= 0x0f;
*dst |= srcval;
src += src_step;
srcbit += srcbit_step;
if (srcbit == 8) {
src++;
srcbit = 0;
}
dst += dst_step;
dstbit += dstbit_step;
if (dstbit == 8) {
dst++;
dstbit = 0;
}
}
break;
}
case 8: {
while (samples-- > 0) {
*dst = *src;
src += src_step;
dst += dst_step;
}
break;
}
case 16: {
while (samples-- > 0) {
*(u_int16_t*)dst = *(const u_int16_t*)src;
src += src_step;
dst += dst_step;
}
break;
}
case 24:
while (samples-- > 0) {
*(dst + 0) = *(src + 0);
*(dst + 1) = *(src + 1);
*(dst + 2) = *(src + 2);
src += src_step;
dst += dst_step;
}
break;
case 32: {
while (samples-- > 0) {
*(u_int32_t*)dst = *(const u_int32_t*)src;
src += src_step;
dst += dst_step;
}
break;
}
case 64: {
while (samples-- > 0) {
*(u_int64_t*)dst = *(const u_int64_t*)src;
src += src_step;
dst += dst_step;
}
break;
}
default:
SNDMSG("invalid format width %d", width);
return -EINVAL;
}
return 0;
}
----------------------------snd_pcm_area_copy------------------------------------
} else {
snd_pcm_area_copy(dst_start, dst_offset,
src_start, src_offset,
frames, format);
src_areas = src_start + 1;
dst_areas = dst_start + 1;
channels--;
}
}
return 0;
}
-------------------------snd_pcm_areas_copy---------------------------------------
result = snd_pcm_mmap_commit(pcm, pcm_offset, frames);
if (result < 0)
return xfer > 0 ? (snd_pcm_sframes_t)xfer : result;
+++++++++++++++++++++++++++snd_pcm_mmap_commit+++++++++++++++++++++++++++++++++++++
/**
* \brief Application has completed the access to area requested with #snd_pcm_mmap_begin
* \param pcm PCM handle
* \param offset area offset in area steps (== frames)
* \param frames area portion size in frames
* \return count of transferred frames otherwise a negative error code
*
* You should pass this function the offset value that
* snd_pcm_mmap_begin() returned. The frames parameter should hold the
* number of frames you have written or read to/from the audio
* buffer. The frames parameter must never exceed the contiguous frames
* count that snd_pcm_mmap_begin() returned. Each call to snd_pcm_mmap_begin()
* must be followed by a call to snd_pcm_mmap_commit().
*
* Example:
\code
double phase = 0;
const snd_pcm_area_t *areas;
snd_pcm_sframes_t avail, size, commitres;
snd_pcm_uframes_t offset, frames;
int err;
avail = snd_pcm_avail_update(pcm);
if (avail < 0)
error(avail);
// at this point, we can transfer at least 'avail' frames
// we want to process frames in chunks (period_size)
if (avail < period_size)
goto _skip;
size = period_size;
// it is possible that contiguous areas are smaller, thus we use a loop
while (size > 0) {
frames = size;
err = snd_pcm_mmap_begin(pcm_handle, &areas, &offset, &frames);
if (err < 0)
error(err);
// this function fills the areas from offset with count of frames
generate_sine(areas, offset, frames, &phase);
commitres = snd_pcm_mmap_commit(pcm_handle, offset, frames);
if (commitres < 0 || commitres != frames)
error(commitres >= 0 ? -EPIPE : commitres);
size -= frames;
}
_skip:
\endcode
*
* Look to the \ref example_test_pcm "Sine-wave generator" example
* for more details about the generate_sine function.
*/
snd_pcm_sframes_t snd_pcm_mmap_commit(snd_pcm_t *pcm,
snd_pcm_uframes_t offset,
snd_pcm_uframes_t frames)
{
assert(pcm);
if (CHECK_SANITY(offset != *pcm->appl.ptr % pcm->buffer_size)) {
SNDMSG("commit offset (%ld) doesn't match with appl_ptr (%ld) %% buf_size (%ld)",
offset, *pcm->appl.ptr, pcm->buffer_size);
return -EPIPE;
}
if (CHECK_SANITY(frames > snd_pcm_mmap_avail(pcm))) {
SNDMSG("commit frames (%ld) overflow (avail = %ld)", frames,
snd_pcm_mmap_avail(pcm));
return -EPIPE;
}
return pcm->fast_ops->mmap_commit(pcm->fast_op_arg, offset, frames);
}
---------------------------snd_pcm_mmap_commit-------------------------------------
offset += result;
xfer += result;
size -= result;
}
return (snd_pcm_sframes_t)xfer;
}
---------------------------func是传入的参数snd_pcm_mmap_write_areas-------------------------------------
if (err < 0)
break;
frames = err;
if (state == SND_PCM_STATE_PREPARED) {
snd_pcm_sframes_t hw_avail = pcm->buffer_size - avail;
hw_avail += frames;
/* some plugins might automatically start the stream */
state = snd_pcm_state(pcm);
if (state == SND_PCM_STATE_PREPARED &&
hw_avail >= (snd_pcm_sframes_t) pcm->start_threshold) {
err = snd_pcm_start(pcm);
if (err < 0)
goto _end;
}
}
offset += frames;
size -= frames;
xfer += frames;
}
_end:
return xfer > 0 ? (snd_pcm_sframes_t) xfer : snd_pcm_check_error(pcm, err);
}
----------------------------snd_pcm_write_areas------------------------------------
}
-------------------------snd_pcm_mmap_writei---------------------------------------
++++++++++++++++++++++++++snd_pcm_writei++++++++++++++++++++++++++++++++++++++
/**
* \brief Write interleaved frames to a PCM
* \param pcm PCM handle
* \param buffer frames containing buffer
* \param size frames to be written
* \return a positive number of frames actually written otherwise a
* negative error code
* \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING)
* \retval -EPIPE an underrun occurred
* \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery)
*
* If the blocking behaviour is selected and it is running, then routine waits until
* all requested frames are played or put to the playback ring buffer.
* The returned number of frames can be less only if a signal or underrun occurred.
*
* If the non-blocking behaviour is selected, then routine doesn't wait at all.
*/
snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
assert(pcm);
assert(size == 0 || buffer);
if (CHECK_SANITY(! pcm->setup)) {
SNDMSG("PCM not set up");
return -EIO;
}
if (pcm->access != SND_PCM_ACCESS_RW_INTERLEAVED) {
SNDMSG("invalid access type %s", snd_pcm_access_name(pcm->access));
return -EINVAL;
}
return _snd_pcm_writei(pcm, buffer, size);
+++++++++++++++++++++++++++_snd_pcm_writei+++++++++++++++++++++++++++++++++++++
static inline snd_pcm_sframes_t _snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
return pcm->fast_ops->writei(pcm->fast_op_arg, buffer, size);
}
---------------------------_snd_pcm_writei-------------------------------------
}
--------------------------snd_pcm_writei--------------------------------------
----------------------------------------------------------------
if (n == -EBADFD) {
// Somehow the stream is in a bad state. The driver probably
// has a bug and snd_pcm_recover() doesn't seem to handle this.
mHandle->module->open(mHandle, mHandle->curDev, mHandle->curMode);
if (aDev && aDev->recover) aDev->recover(aDev, n);
}
else if (n < 0) {
if (mHandle->handle) {
LOGW("underrun and do recovery.....");
// snd_pcm_recover() will return 0 if successful in recovering from
// an error, or -errno if the error was unrecoverable.
n = snd_pcm_recover(mHandle->handle, n, 1);
if (aDev && aDev->recover) aDev->recover(aDev, n);
if (n) return static_cast<ssize_t>(n);
}
}
else {
mFrameCount += n;
sent += static_cast<ssize_t>(snd_pcm_frames_to_bytes(mHandle->handle, n));
}
} while (mHandle->handle && sent < bytes);
return sent;
}
###########################################################
&&&&&&&&&&&&&&&&&&&&&&&总结&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
在open output stream的时候,会创建一个snd_pcm_t的结构体。
write数据的时候,有两中方式:
1、mmap方式,其实是往结构体的stopped_areas或者running_areas成员中copy数据。
写完后,通过commit函数通知底层:
pcm->fast_ops->mmap_commit
2、否则,直接调用结构体的writei函数写数据:
pcm->fast_ops->writei
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&