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等时
相关推荐
用户69371750013842 小时前
4.Kotlin 流程控制:强大的 when 表达式:取代 Switch
android·后端·kotlin
用户69371750013842 小时前
5.Kotlin 流程控制:循环的艺术:for 循环与区间 (Range)
android·后端·kotlin
雨白5 小时前
Jetpack Compose 实战:自定义自适应分段按钮 (Segmented Button)
android·android jetpack
AskHarries5 小时前
RevenueCat 接入 Google Play 订阅全流程详解(2025 最新)
android·flutter·google
The best are water5 小时前
MySQL FEDERATED引擎跨服务器数据同步完整方案
android·服务器·mysql
消失的旧时光-19436 小时前
我如何理解 Flutter 本质
android·前端·flutter
czhc11400756637 小时前
C#1119记录 类 string.Split type.TryParse(String,out type 变量)
android·c#
豆豆豆大王8 小时前
Android SQLite 数据库开发完全指南:从核心概念到高级操作
android·sqlite·数据库开发
_李小白8 小时前
【Android FrameWork】延伸阅读:AssetManager
android