ALSADMA缓冲区分析

    ALSA应用程序(播放器)调用ALSA lib库中的函数snd_pcm_writei()向声卡硬件(或虚拟的)写入交错(write后的i代表interleaved)数据。在ALSA lib中最后会调到snd_pcm_hw_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)函数,这个函数调用通用的ioctl接口:
snd_pcm_lib_write1(substream, (unsigned long)bufs, frames,  nonblock, snd_pcm_lib_writev_transfer)函数,其中snd_pcm_lib_writev_transfer是个函数指针。这个函数很关键,其中包括下面的代码:
static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, 
                        unsigned long data,
                        snd_pcm_uframes_t size,
                        int nonblock,
                        transfer_f transfer)
{
    struct snd_pcm_runtime *runtime = substream->runtime;
    snd_pcm_uframes_t xfer = 0;
    snd_pcm_uframes_t offset = 0;
    int err = 0;

    if (size == 0)
        return 0;

    snd_pcm_stream_lock_irq(substream);
    switch (runtime->status->state) {
    case SNDRV_PCM_STATE_PREPARED:
    case SNDRV_PCM_STATE_RUNNING:
    case SNDRV_PCM_STATE_PAUSED:
        break;
    case SNDRV_PCM_STATE_XRUN:
        err = -EPIPE;
        goto _end_unlock;
    case SNDRV_PCM_STATE_SUSPENDED:
        err = -ESTRPIPE;
        goto _end_unlock;
    default:
        err = -EBADFD;
        goto _end_unlock;
    }

    while (size > 0) {
        snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
        snd_pcm_uframes_t avail;
        snd_pcm_uframes_t cont;
        if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
            snd_pcm_update_hw_ptr(substream);
        avail = snd_pcm_playback_avail(runtime);
        if (!avail) {
            if (nonblock) {
                err = -EAGAIN;
                goto _end_unlock;
            }
            err = wait_for_avail_min(substream, &avail);
            if (err < 0)
                goto _end_unlock;
        }
        frames = size > avail ? avail : size;
        cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;
        if (frames > cont)
            frames = cont;
        snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);
        appl_ptr = runtime->control->appl_ptr;
        appl_ofs = appl_ptr % runtime->buffer_size;
        snd_pcm_stream_unlock_irq(substream);
        if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
            goto _end;
        snd_pcm_stream_lock_irq(substream);
        switch (runtime->status->state) {
        case SNDRV_PCM_STATE_XRUN:
            err = -EPIPE;
            goto _end_unlock;
        case SNDRV_PCM_STATE_SUSPENDED:
            err = -ESTRPIPE;
            goto _end_unlock;
        default:
            break;
        }
        appl_ptr += frames;
        if (appl_ptr >= runtime->boundary)
            appl_ptr -= runtime->boundary;
        runtime->control->appl_ptr = appl_ptr;
        if (substream->ops->ack)
            substream->ops->ack(substream);

        offset += frames;
        size -= frames;
        xfer += frames;
        if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
            snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
            err = snd_pcm_start(substream);
            if (err < 0)
                goto _end_unlock;
        }
    }
 _end_unlock:
    snd_pcm_stream_unlock_irq(substream);
 _end:
    return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
}

snd_pcm_playback_avail是个内敛函数,在include/sound/pcm.h中定义:
/*
 *  result is: 0 ... (boundary - 1)
 */
static inline snd_pcm_uframes_t snd_pcm_playback_avail(struct snd_pcm_runtime *runtime)
{
    snd_pcm_sframes_t avail = runtime->status->hw_ptr + runtime->buffer_size - runtime->control->appl_ptr;
    printk("runtime->status->hw_ptr:%lu  runtime->buffer_size:%lu,  runtime->control->appl_ptr:%lu runtime->boundary:%lu", 
            runtime->status->hw_ptr,  runtime->buffer_size, runtime->control->appl_ptr, runtime->boundary);
    if (avail < 0)
        avail += runtime->boundary;
    else if ((snd_pcm_uframes_t) avail >= runtime->boundary)
        avail -= runtime->boundary;
    return avail;
}
    上面的函数代码,可以帮助我们理解 alsa 中的数据结构成员所代表的含义。很显然,上面的函数的目的就是返回当前hardware buffer中用户程序可写的帧(frames)数。runtime->status-hw_ptr应该表示硬件指针,指向硬件已经处理(播放/录音)过的数据的位置,runtime->control->buffer_size是整个buffer的大小,而runtime->control->appl_ptr是用户程序已经处理(读/写)过的数据的位置。appl_ptr之后的空间,一直到buffer末尾,这一段是可以写入的(注意这个函数名中有playback,只用于播放),这一段长度为runtime->buffer_size - runtime->control->appl_ptr;另外hw_ptr之前的空间也是可写的(其中的数据已经被播放),所以整个可用的长度为 runtime->status->hw_ptr + runtime->buffer_size - runtime->control->appl_ptr。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值