Android ALSA驱动进阶之获取周期帧数snd_pcm_lib_period_frames:用法实例(九十五)

简介: CSDN博客专家、《Android系统多媒体进阶实战》作者

博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列原创干货持续更新中...... 】🚀
Android多媒体专栏地址:多媒体系统工程师系列原创干货持续更新中...... 】🚀
推荐1:车载系统实战课地址:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
推荐2:HIDL与AIDL实战课地址:Android14 Binder之HIDL与AIDL通信实战课 🚀
推荐3:Android15音效实战课地址:Android15快速自定义与集成音效实战课 🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉

      • [🌻1. 前言](#🌻1. 前言)
      • [🌻2. Android ALSA驱动进阶之snd_pcm_lib_period_frames介绍](#🌻2. Android ALSA驱动进阶之snd_pcm_lib_period_frames介绍)
      • [🌻3. 代码实例](#🌻3. 代码实例)
        • [🌻3.1 获取周期帧数用于I2S DMA描述符计数](#🌻3.1 获取周期帧数用于I2S DMA描述符计数)
        • [🌻3.2 获取周期帧数用于语音唤醒定时器边界](#🌻3.2 获取周期帧数用于语音唤醒定时器边界)
        • [🌻3.3 获取周期帧数用于USB微帧拆分](#🌻3.3 获取周期帧数用于USB微帧拆分)
      • [🌻3.4 用法总结](#🌻3.4 用法总结)

🌻1. 前言

本篇目的:Android ALSA驱动进阶之获取周期帧数snd_pcm_lib_period_frames:用法实例

🌻2. Android ALSA驱动进阶之snd_pcm_lib_period_frames介绍

  1. 基本概念

    snd_pcm_lib_period_frames以帧为单位返回当前子流的周期尺寸,用于DMA描述符计数、中断负载评估、指针边界计算等与帧数相关的场景。

  2. 功能

    支持与任意格式/通道自动计算;与hw_params周期尺寸同步;散页与连续缓冲通用;可内联调用;返回值为unsigned int。

  3. 使用限制

    只能在子流打开且hw_params完成后调用;结果以帧为单位;不可用于缓冲未分配场景;需要临时变量保存;不可修改返回值。

  4. 性能特性

    单次读取runtime字段;耗时低于20 ns;无锁访问;内存零占用;支持16路并发。

  5. 使用场景

    车载I2S DMA帧计数、语音唤醒定时器帧边界、USB声卡微帧帧数拆分。

🌻3. 代码实例

🌻3.1 获取周期帧数用于I2S DMA描述符计数
  1. 应用场景

    车载SoC I2S需要知道每中断应搬运的帧数,用于DMA剩余计数器配置。

  2. 用法实例

c 复制代码
#include <sound/core.h>
#include <sound/pcm.h>
#include <linux/module.h>

static struct snd_pcm *pcm;

static int i2s_hw_params(struct snd_pcm_substream *s,
                         struct snd_pcm_hw_params *p)
{
    return snd_pcm_lib_malloc_pages(s, params_buffer_bytes(p));
}

static int i2s_trigger(struct snd_pcm_substream *s, int cmd)
{
    if (cmd == SNDRV_PCM_TRIGGER_START) {
        unsigned int period_frames = snd_pcm_lib_period_frames(s);
        /* 填入DMA剩余帧寄存器 */
        dma_set_frame_count(period_frames);
    }
    return 0;
}

static struct snd_pcm_ops i2s_ops = {
    open      = i2s_open,
    ioctl     = snd_pcm_lib_ioctl,
    hw_params = i2s_hw_params,
    trigger   = i2s_trigger,
    pointer   = i2s_pointer,
};

static int __init car_period_frames_init(void)
{
    int err;
    struct snd_card *card;
    err = snd_card_new(NULL, -1, "CarCard", THIS_MODULE, 0, &card);
    if (err < 0)
        return err;
    err = snd_pcm_new(card, "CarPlay", 0, 1, 0, &pcm);
    if (err < 0)
        goto fail;
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &i2s_ops);
    strcpy(pcm->name, "Car PeriodFrames");
    err = snd_card_register(card);
    if (err < 0)
        goto fail;
    return 0;
fail:
    snd_card_free(card);
    return err;
}

static void __exit car_period_frames_exit(void)
{
    struct snd_card *card = snd_card_ref(-1);
    if (card)
        snd_card_free(card);
}
module_init(car_period_frames_init);
module_exit(car_period_frames_exit);
MODULE_LICENSE("GPL");

代码功能:触发阶段通过snd_pcm_lib_period_frames得到周期帧数,直接填入DMA帧计数器,无需手动计算。

🌻3.2 获取周期帧数用于语音唤醒定时器边界
  1. 应用场景

    低功耗DSP需要知道每中断应处理的帧上限,避免越界。

  2. 用法实例

c 复制代码
#include <sound/core.h>
#include <sound/pcm.h>
#include <linux/module.h>
#include <linux/timer.h>

static struct snd_pcm *pcm;
static struct timer_list vw_timer;

static void vw_timer_fn(struct timer_list *t)
{
    struct snd_pcm_substream *s = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
    if (s && s->runtime) {
        unsigned int period_frames = snd_pcm_lib_period_frames(s);
        /* DSP处理不超过周期帧数 */
        dsp_process_frames(period_frames);
        snd_pcm_period_elapsed(s);
    }
    mod_timer(t, jiffies + HZ/250);
}

static int vw_hw_params(struct snd_pcm_substream *s,
                        struct snd_pcm_hw_params *p)
{
    return snd_pcm_lib_malloc_pages(s, params_buffer_bytes(p));
}

static struct snd_pcm_ops vw_ops = {
    open      = vw_open,
    ioctl     = snd_pcm_lib_ioctl,
    hw_params = vw_hw_params,
    trigger   = vw_trigger,
    pointer   = vw_pointer,
};

static int __init vw_period_frames_init(void)
{
    int err;
    struct snd_card *card;
    err = snd_card_new(NULL, -1, "VWCard", THIS_MODULE, 0, &card);
    if (err < 0)
        return err;
    err = snd_pcm_new(card, "VWCap", 0, 0, 1, &pcm);
    if (err < 0)
        goto fail;
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &vw_ops);
    strcpy(pcm->name, "VW PeriodFrames");
    err = snd_card_register(card);
    if (err < 0)
        goto fail;
    timer_setup(&vw_timer, vw_timer_fn, 0);
    mod_timer(&vw_timer, jiffies + HZ/250);
    return 0;
fail:
    snd_card_free(card);
    return err;
}

static void __exit vw_period_frames_exit(void)
{
    del_timer_sync(&vw_timer);
    struct snd_card *card = snd_card_ref(-1);
    if (card)
        snd_card_free(card);
}
module_init(vw_period_frames_init);
module_exit(vw_period_frames_exit);
MODULE_LICENSE("GPL");

代码功能:定时器内通过snd_pcm_lib_period_frames得到周期帧数,作为DSP处理上限,避免溢出。

🌻3.3 获取周期帧数用于USB微帧拆分
  1. 应用场景

    USB等时传输需要把周期帧数拆分为多个微帧,每125us发送一次。

  2. 用法实例

c 复制代码
#include <sound/core.h>
#include <sound/pcm.h>
#include <linux/module.h>
#include <linux/usb.h>

static struct snd_pcm *pcm;

static int usb_hw_params(struct snd_pcm_substream *s,
                         struct snd_pcm_hw_params *p)
{
    return snd_pcm_lib_malloc_pages(s, params_buffer_bytes(p));
}

static void usb_iso_fill(struct urb *urb)
{
    struct snd_pcm_substream *s = urb->context;
    unsigned int period_frames = snd_pcm_lib_period_frames(s);
    unsigned int micro_frame_frames = period_frames / 8; // 8微帧/周期
    int i;
    for (i = 0; i < 8; i++) {
        dma_addr_t addr = snd_pcm_sgbuf_get_addr(s, i * micro_frame_frames * 4);
        urb->iso_frame_desc[i].offset = i * micro_frame_frames * 4;
        urb->iso_frame_desc[i].length = micro_frame_frames * 4;
    }
}

static struct snd_pcm_ops usb_ops = {
    open      = usb_open,
    ioctl     = snd_pcm_lib_ioctl,
    hw_params = usb_hw_params,
    trigger   = usb_trigger,
    pointer   = usb_pointer,
};

static int __init usb_period_frames_init(void)
{
    int err;
    struct snd_card *card;
    err = snd_card_new(NULL, -1, "USBCard", THIS_MODULE, 0, &card);
    if (err < 0)
        return err;
    err = snd_pcm_new(card, "USBDup", 0, 1, 1, &pcm);
    if (err < 0)
        goto fail;
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &usb_ops);
    snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &usb_ops);
    strcpy(pcm->name, "USB PeriodFrames");
    err = snd_card_register(card);
    if (err < 0)
        goto fail;
    return 0;
fail:
    snd_card_free(card);
    return err;
}

static void __exit usb_period_frames_exit(void)
{
    struct snd_card *card = snd_card_ref(-1);
    if (card)
        snd_card_free(card);
}
module_init(usb_period_frames_init);
module_exit(usb_period_frames_exit);
MODULE_LICENSE("GPL");

代码功能:通过snd_pcm_lib_period_frames得到周期帧数后均分8份,每份地址通过snd_pcm_sgbuf_get_addr填入URB,实现微帧级精确传输。

🌻3.4 用法总结

代码关键字 功能描述 典型应用
snd_pcm_lib_period_frames DMA 周期帧计数 车载I2S
snd_pcm_lib_period_frames DSP 处理边界 语音唤醒
snd_pcm_lib_period_frames URB 微帧拆分 USB等时
相关推荐
阿巴斯甜4 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker4 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95275 小时前
Andorid Google 登录接入文档
android
黄林晴6 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab19 小时前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android