Android内核进阶之获取当前PCM周期snd_pcm_lib_period_bytes:用法实例(九十三)

简介: 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内核进阶之snd_pcm_lib_period_bytes介绍](#🌻2. Android内核进阶之snd_pcm_lib_period_bytes介绍)
      • [🌻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内核进阶之获取当前PCM周期snd_pcm_lib_period_bytes:用法实例

🌻2. Android内核进阶之snd_pcm_lib_period_bytes介绍

  1. 基本概念

    snd_pcm_lib_period_bytes以字节为单位返回当前子流的周期尺寸,用于DMA描述符填充、缓存边界计算、中断负载评估等场景。

  2. 功能

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

  3. 使用限制

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

  4. 性能特性

    单次乘法运算;耗时低于20 ns;无锁访问runtime字段;内存零占用;支持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_bytes = snd_pcm_lib_period_bytes(s);
        /* 填入DMA控制器长度寄存器 */
        dma_set_length(period_bytes);
    }
    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_bytes_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 PeriodBytes");
    err = snd_card_register(card);
    if (err < 0)
        goto fail;
    return 0;
fail:
    snd_card_free(card);
    return err;
}

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

代码功能:触发阶段通过snd_pcm_lib_period_bytes得到周期字节数,直接填入DMA长度寄存器,无需手动计算。

🌻3.2 获取周期字节用于语音唤醒散页边界
  1. 应用场景

    DSP需要知道每中断应处理的散页字节上限,避免越界。

  2. 用法实例

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

static struct snd_pcm *pcm;

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 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_bytes = snd_pcm_lib_period_bytes(s);
        /* DSP处理不超过周期字节 */
        dsp_process_slice(period_bytes);
        snd_pcm_period_elapsed(s);
    }
    mod_timer(t, jiffies + HZ/250);
}

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_bytes_init(void)
{
    int err;
    struct snd_card *card;
    static struct timer_list vw_timer;
    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 PeriodBytes");
    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_bytes_exit(void)
{
    struct snd_card *card = snd_card_ref(-1);
    if (card)
        snd_card_free(card);
}
module_init(vw_period_bytes_init);
module_exit(vw_period_bytes_exit);
MODULE_LICENSE("GPL");

代码功能:定时器内通过snd_pcm_lib_period_bytes得到周期字节,作为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_bytes = snd_pcm_lib_period_bytes(s);
    unsigned int micro_frame_bytes = period_bytes / 8; // 8微帧/周期
    int i;
    for (i = 0; i < 8; i++) {
        dma_addr_t addr = snd_pcm_sgbuf_get_addr(s, i * micro_frame_bytes);
        urb->iso_frame_desc[i].offset = i * micro_frame_bytes;
        urb->iso_frame_desc[i].length = micro_frame_bytes;
    }
}

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_bytes_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 PeriodBytes");
    err = snd_card_register(card);
    if (err < 0)
        goto fail;
    return 0;
fail:
    snd_card_free(card);
    return err;
}

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

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

🌻3.4 用法总结

代码关键字 功能描述 典型应用
snd_pcm_lib_period_bytes DMA 周期字节长度 车载I2S
snd_pcm_lib_period_bytes DSP 处理边界 语音唤醒
snd_pcm_lib_period_bytes 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