audio的replay模式不能播放长度小于内存块大小的数据吗?

发布于 2020-09-10 11:43:19

使用_audio_dev_write启动音频数据replay。从代码看,在这个函数中是要把发送的数据按照replay内存池中内存块的大小进行分割,如果数据量能够满足内存块大小,就作为一帧数据放入replay->queue中,而剩余不足一个内存块大小的数据或者要发送的数据总量小于一个内存块大小时,就不会把数据放入replay->queue,而是留在replay->write_data中。

/* components/drivers/audio/audio.c */

static rt_size_t _audio_dev_write(struct rt_device *dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    ...
    
    /* push a new frame to replay data queue */
    ptr = (rt_uint8_t *)buffer;
    block_size = RT_AUDIO_REPLAY_MP_BLOCK_SIZE;

    rt_mutex_take(&audio->replay->lock, RT_WAITING_FOREVER);
    while (index < size)
    {
        /* request buffer from replay memory pool */
        if (audio->replay->write_index % block_size == 0)
        {
            audio->replay->write_data = rt_mp_alloc(audio->replay->mp, RT_WAITING_FOREVER);
            memset(audio->replay->write_data, 0, block_size);
        }

        /* copy data to replay memory pool */
        remain_bytes = MIN((block_size - audio->replay->write_index), (size - index));
        memcpy(&audio->replay->write_data[audio->replay->write_index], &ptr[index], remain_bytes);

        index += remain_bytes;
        audio->replay->write_index += remain_bytes;
        audio->replay->write_index %= block_size;

        if (audio->replay->write_index == 0)
        {
            rt_data_queue_push(&audio->replay->queue,
                               audio->replay->write_data,
                               block_size,
                               RT_WAITING_FOREVER);
        }
    }
    ...
}

而实际启动数据发送的过程是这样的:底层驱动先用DMA发一段全0的数据,然后在DMA完成中断中调用_audio_send_replay_frame发送数据,在_audio_send_replay_frame函数中,如果replay->queue为空,就只会发送全0数据。也就是说不足一个内存块大小的数据并没有被发送。

/* components/drivers/audio/audio.c */

static rt_err_t _audio_send_replay_frame(struct rt_audio_device *audio)
{
    ...

    buf_info = &audio->replay->buf_info;
    /* save current pos */
    position = audio->replay->pos;
    dst_size = buf_info->block_size;

    /* check repaly queue is empty */
    if (rt_data_queue_peak(&audio->replay->queue, (const void **)&data, &src_size) != RT_EOK)
    {
        /* ack stop event */
        if (audio->replay->event & REPLAY_EVT_STOP)
            rt_completion_done(&audio->replay->cmp);

        /* send zero frames */
        memset(&buf_info->buffer[audio->replay->pos], 0, dst_size);

        audio->replay->pos += dst_size;
        audio->replay->pos %= buf_info->total_size;
    }
    else
    {
        ...
    }

    if (audio->ops->transmit != RT_NULL)
    {
        if (audio->ops->transmit(audio, &buf_info->buffer[position], RT_NULL, dst_size) != dst_size)
            result = -RT_ERROR;
    }

    return result;
}

请帮忙看一下上面的理解是哪里有问题?

查看更多

关注者
0
被浏览
272
liu2guang
liu2guang 认证专家 2020-09-10

点赞,分析没有问题的,还是按照使用场景来分析:

场景

  1. 暂停播放:暂停播放会将最后不满一帧的数据缓存到audio框架的缓存中,但是接着播放写入数据时就可以将不满的数据和后续的一起写入到硬件codec中,这样数据是连贯的不会出现pop音
  2. 停止播放:停止播放后不满一帧的没有写入,没有关系,因为用户主观上已经停止掉了,不需要flush这样的操作,这样停止更快
  3. 暂停播放 + 切换歌曲:这里其实就有问题了,不满的数据+另外一首歌曲的数据,就会出现音频的不连续性,这样从听感上来说就是 pop 一声,所以切换歌曲的情况下需要有一个类似 clean 的操作,或者是暂停的时候 flush 下

还有标题中“audio的replay模式不能播放长度小于内存块大小的数据吗”其实没有什么意义,因为你的缓存一般都是配置程20ma大小的缓存区,实际使用是没有这么短的音频的。

1 个回答

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览