Android ALSA驱动进阶之获取采样格式位宽snd_pcm_format_width:用法实例(九十八)

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

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

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

🍉🍉🍉文章目录🍉🍉🍉

      • [🌻1. 前言](#🌻1. 前言)
      • [🌻2. Android ALSA驱动进阶之snd_pcm_format_width介绍](#🌻2. Android ALSA驱动进阶之snd_pcm_format_width介绍)
      • [🌻3. 代码实例](#🌻3. 代码实例)
        • [🌻3.1 在车载系统中计算缓冲大小](#🌻3.1 在车载系统中计算缓冲大小)
        • [🌻3.2 在语音唤醒中进行数据转换](#🌻3.2 在语音唤醒中进行数据转换)
        • [🌻3.3 在USB声卡中计算字节传输](#🌻3.3 在USB声卡中计算字节传输)
      • [🌻3.4 用法总结](#🌻3.4 用法总结)

🌻1. 前言

本篇目的:Android ALSA驱动进阶之获取采样格式位宽snd_pcm_format_width:用法实例

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

  1. 基本概念

    snd_pcm_format_width根据enum snd_pcm_format返回采样格式的位宽,用于计算缓冲大小、DMA传输字节数、数据转换等场景。

  2. 功能

    支持S8到FLOAT_LE全部格式;返回采样位宽;与内核print格式无缝兼容;可内联调用;返回值为int。

  3. 使用限制

    传入值必须为合法enum snd_pcm_format;不可用于中断上下文日志;需要临时变量保存;不支持自定义格式。

  4. 性能特性

    单次表查询;耗时低于50 ns;内存只读零拷贝;支持16路并发;编码体积增加不到16字节。

  5. 使用场景

    车载缓冲计算、语音唤醒数据转换、USB声卡字节计算。

🌻3. 代码实例

🌻3.1 在车载系统中计算缓冲大小
  1. 应用场景

    车载系统需要根据采样格式的位宽计算缓冲大小,以确保缓冲区足够大以存储音频数据。

  2. 用法实例

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

static struct snd_pcm *pcm;

static int car_hw_params(struct snd_pcm_substream *s,
                         struct snd_pcm_hw_params *p)
{
    int width = snd_pcm_format_width(params_format(p));
    int channels = params_channels(p);
    int rate = params_rate(p);
    int buffer_size = params_buffer_size(p);
    int buffer_bytes = buffer_size * channels * width / 8;
    pr_info("Buffer size: %d bytes\n", buffer_bytes);
    return snd_pcm_lib_malloc_pages(s, buffer_bytes);
}

static struct snd_pcm_ops car_ops = {
    open      = car_open,
    ioctl     = snd_pcm_lib_ioctl,
    hw_params = car_hw_params,
    trigger   = car_trigger,
    pointer   = car_pointer,
};

static int __init car_width_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, &car_ops);
    strcpy(pcm->name, "Car Width");
    err = snd_card_register(card);
    if (err < 0)
        goto fail;
    return 0;
fail:
    snd_card_free(card);
    return err;
}

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

代码功能:通过snd_pcm_format_width得到采样格式的位宽,计算缓冲大小并打印,确保缓冲区足够大。

🌻3.2 在语音唤醒中进行数据转换
  1. 应用场景

    语音唤醒模块需要将音频数据从一种格式转换为另一种格式,需要知道源格式的位宽。

  2. 用法实例

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

static struct snd_pcm *pcm;

static int vw_copy(struct snd_pcm_substream *s,
                   int channel, unsigned long pos,
                   void __user *buf, unsigned long frames)
{
    int width = snd_pcm_format_width(s->runtime->format);
    int channels = s->runtime->channels;
    int frame_size = channels * width / 8;
    char *src = s->runtime->dma_area + pos * frame_size;
    char *dst = (char __user *)buf;
    for (unsigned long i = 0; i < frames; i++) {
        // 示例:16位转8位
        if (width == 16) {
            int16_t sample = *(int16_t *)src;
            int8_t converted = sample >> 8;
            if (copy_to_user(dst, &converted, sizeof(converted)))
                return -EFAULT;
        }
        src += frame_size;
        dst += frame_size / 2; // 假设目标格式位宽为8位
    }
    return frames;
}

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,
    copy      = vw_copy,
};

static int __init vw_width_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 Width");
    err = snd_card_register(card);
    if (err < 0)
        goto fail;
    return 0;
fail:
    snd_card_free(card);
    return err;
}

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

代码功能:通过snd_pcm_format_width得到采样格式的位宽,进行数据转换,确保数据格式正确。

🌻3.3 在USB声卡中计算字节传输
  1. 应用场景

    USB声卡需要根据采样格式的位宽计算每帧的字节数,以便正确配置DMA传输。

  2. 用法实例

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

static struct snd_pcm *pcm;

static int usb_hw_params(struct snd_pcm_substream *s,
                         struct snd_pcm_hw_params *p)
{
    int width = snd_pcm_format_width(params_format(p));
    int channels = params_channels(p);
    int frame_size = channels * width / 8;
    pr_info("Frame size: %d bytes\n", frame_size);
    return snd_pcm_lib_malloc_pages(s, params_buffer_bytes(p));
}

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

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

代码功能:通过snd_pcm_format_width得到采样格式的位宽,计算每帧的字节数并打印,确保DMA传输配置正确。

🌻3.4 用法总结

代码关键字 功能描述 典型应用
snd_pcm_format_width 缓冲计算 计算缓冲大小 车载系统
snd_pcm_format_width 数据转换 转换采样格式 语音唤醒
snd_pcm_format_width 字节计算 配置DMA传输 USB声卡
相关推荐
莫比乌斯环3 小时前
【日常随笔】Android 跳离行为分析 - Instrumentation
android·架构·代码规范
aningxiaoxixi3 小时前
android 媒体之 MediaSession
android·媒体
GoldenPlayer3 小时前
Android文件权限报错
android
Jomurphys3 小时前
Compose 适配 - 全屏显示 EdgeToEdge
android
ii_best3 小时前
「安卓开发辅助工具按键精灵」xml全分辨率插件jsd插件脚本教程
android·xml·开发语言·编辑器·安卓
消失的旧时光-19433 小时前
从 Android 回调到 C 接口:函数指针 + void* self 的一次彻底理解
android·c语言·开发语言
dvlinker3 小时前
动态代理技术实战测评—高效解锁Zillow房价历史
android·java·数据库
峥嵘life3 小时前
Android16 EDLA 认证BTS测试Failed解决总结
android·java·linux·运维·学习
二流小码农3 小时前
鸿蒙开发:自定义一个圆形动画菜单
android·ios·harmonyos