零基础入门多媒体音频(6)-alsa(2)

news/2024/6/20 20:16:18/文章来源:https://blog.csdn.net/weixin_52370850/article/details/137256926

PCM接口
ALSA的PCM中间层非常有用,每个驱动只需要实现底层的功能来访问硬件。要使用PCM层,你需要先引用 <sound/pcm.h>头文件。此外,如果你要使用和hw_param相关的函数,<sound/pcm_params.h>也是必须的。

每个声卡设备最多拥有4个PCM实例。一个PCM实例对应一个PCM设备文件。实例数量的约束来自linux设备序号可用的位大小。当64bit的设备序号开始使用时,我们就可以支持更多的PCM实例。

PCM实例包含PCM回放和录制流。每个PCM流包含一个或更多的PCM子流。一些声卡支持多路回放功能。比如,emu10k1 支持32路立体声播放子流。这种情况下,每次打开动作,一路空闲的子流会被自动选择并打开。如果只有一路子流存在并且已经打开过,那么后续的打开动作将会阻塞或者返回错误码EAGAIN 。但在驱动中不需要关注这些细节。PCM中间岑会处理这些工作。


#include <sound/pcm.h>
..../* hardware definition */
static struct snd_pcm_hardware snd_mychip_playback_hw = {.info = (SNDRV_PCM_INFO_MMAP |SNDRV_PCM_INFO_INTERLEAVED |SNDRV_PCM_INFO_BLOCK_TRANSFER |SNDRV_PCM_INFO_MMAP_VALID),.formats =          SNDRV_PCM_FMTBIT_S16_LE,.rates =            SNDRV_PCM_RATE_8000_48000,.rate_min =         8000,.rate_max =         48000,.channels_min =     2,.channels_max =     2,.buffer_bytes_max = 32768,.period_bytes_min = 4096,.period_bytes_max = 32768,.periods_min =      1,.periods_max =      1024,
};/* hardware definition */
static struct snd_pcm_hardware snd_mychip_capture_hw = {.info = (SNDRV_PCM_INFO_MMAP |SNDRV_PCM_INFO_INTERLEAVED |SNDRV_PCM_INFO_BLOCK_TRANSFER |SNDRV_PCM_INFO_MMAP_VALID),.formats =          SNDRV_PCM_FMTBIT_S16_LE,.rates =            SNDRV_PCM_RATE_8000_48000,.rate_min =         8000,.rate_max =         48000,.channels_min =     2,.channels_max =     2,.buffer_bytes_max = 32768,.period_bytes_min = 4096,.period_bytes_max = 32768,.periods_min =      1,.periods_max =      1024,
};/* open callback */
static int snd_mychip_playback_open(struct snd_pcm_substream *substream)
{struct mychip *chip = snd_pcm_substream_chip(substream);struct snd_pcm_runtime *runtime = substream->runtime;runtime->hw = snd_mychip_playback_hw;/* more hardware-initialization will be done here */....return 0;
}/* close callback */
static int snd_mychip_playback_close(struct snd_pcm_substream *substream)
{struct mychip *chip = snd_pcm_substream_chip(substream);/* the hardware-specific codes will be here */....return 0;}/* open callback */
static int snd_mychip_capture_open(struct snd_pcm_substream *substream)
{struct mychip *chip = snd_pcm_substream_chip(substream);struct snd_pcm_runtime *runtime = substream->runtime;runtime->hw = snd_mychip_capture_hw;/* more hardware-initialization will be done here */....return 0;
}/* close callback */
static int snd_mychip_capture_close(struct snd_pcm_substream *substream)
{struct mychip *chip = snd_pcm_substream_chip(substream);/* the hardware-specific codes will be here */....return 0;
}/* hw_params callback */
static int snd_mychip_pcm_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *hw_params)
{/* the hardware-specific codes will be here */....return 0;
}/* hw_free callback */
static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream)
{/* the hardware-specific codes will be here */....return 0;
}/* prepare callback */
static int snd_mychip_pcm_prepare(struct snd_pcm_substream *substream)
{struct mychip *chip = snd_pcm_substream_chip(substream);struct snd_pcm_runtime *runtime = substream->runtime;/* set up the hardware with the current configuration* for example...*/mychip_set_sample_format(chip, runtime->format);mychip_set_sample_rate(chip, runtime->rate);mychip_set_channels(chip, runtime->channels);mychip_set_dma_setup(chip, runtime->dma_addr,chip->buffer_size,chip->period_size);return 0;
}/* trigger callback */
static int snd_mychip_pcm_trigger(struct snd_pcm_substream *substream,int cmd)
{switch (cmd) {case SNDRV_PCM_TRIGGER_START:/* do something to start the PCM engine */....break;case SNDRV_PCM_TRIGGER_STOP:/* do something to stop the PCM engine */....break;default:return -EINVAL;}
}/* pointer callback */
static snd_pcm_uframes_t
snd_mychip_pcm_pointer(struct snd_pcm_substream *substream)
{struct mychip *chip = snd_pcm_substream_chip(substream);unsigned int current_ptr;/* get the current hardware pointer */current_ptr = mychip_get_hw_pointer(chip);return current_ptr;
}/* operators */
static struct snd_pcm_ops snd_mychip_playback_ops = {.open =        snd_mychip_playback_open,.close =       snd_mychip_playback_close,.hw_params =   snd_mychip_pcm_hw_params,.hw_free =     snd_mychip_pcm_hw_free,.prepare =     snd_mychip_pcm_prepare,.trigger =     snd_mychip_pcm_trigger,.pointer =     snd_mychip_pcm_pointer,
};/* operators */
static struct snd_pcm_ops snd_mychip_capture_ops = {.open =        snd_mychip_capture_open,.close =       snd_mychip_capture_close,.hw_params =   snd_mychip_pcm_hw_params,.hw_free =     snd_mychip_pcm_hw_free,.prepare =     snd_mychip_pcm_prepare,.trigger =     snd_mychip_pcm_trigger,.pointer =     snd_mychip_pcm_pointer,
};/**  definitions of capture are omitted here...*//* create a pcm device */
static int snd_mychip_new_pcm(struct mychip *chip)
{struct snd_pcm *pcm;int err;err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);if (err < 0)return err;pcm->private_data = chip;strcpy(pcm->name, "My Chip");chip->pcm = pcm;/* set operators */snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,&snd_mychip_playback_ops);snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,&snd_mychip_capture_ops);/* pre-allocation of buffers *//* NOTE: this may fail */snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,&chip->pci->dev,64*1024, 64*1024);return 0;
}调用snd_pcm_new()函数可以获得PCM实例。可以为PCM定义一个构造函数,比如说:static int snd_mychip_new_pcm(struct mychip *chip)
{struct snd_pcm *pcm;int err;err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);if (err < 0)return err;pcm->private_data = chip;strcpy(pcm->name, "My Chip");chip->pcm = pcm;...return 0;
}snd_pcm_new()函数包含六个参数。第一个参数时PCM要绑定的声卡。也就是上篇文章提到并获得的snd_card实例。第二个参数是ID字符串。第三个参数是新PCM的索引。第四个和第五个参数是播放和录制子流的个数。当没有播放和录制子流可用的时候,对应的参数传0.如果芯片支持多路播放或者录制子流,你可以指定更大的数。但在open,close,回调等函数内部要做好处理。当你需要知道在处理哪个子流时,你可以从每个回调函数的参数中获取,参考下面的示例:
struct snd_pcm_substream *substream;
int index = substream->number;当PCM实例创建后,你需要为每个PCM流设置操作结构体。snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,&snd_mychip_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,&snd_mychip_capture_ops);一个典型的操作结构体如下:static struct snd_pcm_ops snd_mychip_playback_ops = {.open =        snd_mychip_pcm_open,.close =       snd_mychip_pcm_close,.hw_params =   snd_mychip_pcm_hw_params,.hw_free =     snd_mychip_pcm_hw_free,.prepare =     snd_mychip_pcm_prepare,.trigger =     snd_mychip_pcm_trigger,.pointer =     snd_mychip_pcm_pointer,
};操作结构体的包含了所有回调函数。
设置完操作结构体后,可以预分配缓冲区(buffer)并设置管理分配模式。执行下面的代码即可。
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,&chip->pci->dev,64*1024, 64*1024);这段代码会默认会分配最多64kB的buffer。另外,pcm->info_flags还可以包含更多的信息。 <sound/asound.h>中定义的SNDRV_PCM_INFO_XXX类型的宏都可以添加到flag中。运行时指针---------PCM的主要信息
当PCM子流被打开,一个PCM运行时就被创建好并分配给子流。通过 substream->runtime就可以访问了。你要用来控制PCM的信息都可以通过运行时指针获得。Hw_params和sw_params配置的拷贝,缓冲区指针。Mmap记录。,自旋锁等。<sound/pcm.h>文件定义了运行时实例。下面是截取的一部分代码。struct _snd_pcm_runtime {/* -- Status -- */struct snd_pcm_substream *trigger_master;snd_timestamp_t trigger_tstamp;       /* trigger timestamp */int overrange;snd_pcm_uframes_t avail_max;snd_pcm_uframes_t hw_ptr_base;        /* Position at buffer restart */snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*//* -- HW params -- */snd_pcm_access_t access;      /* access mode */snd_pcm_format_t format;      /* SNDRV_PCM_FORMAT_* */snd_pcm_subformat_t subformat;        /* subformat */unsigned int rate;            /* rate in Hz */unsigned int channels;                /* channels */snd_pcm_uframes_t period_size;        /* period size */unsigned int periods;         /* periods */snd_pcm_uframes_t buffer_size;        /* buffer size */unsigned int tick_time;               /* tick time */snd_pcm_uframes_t min_align;  /* Min alignment for the format */size_t byte_align;unsigned int frame_bits;unsigned int sample_bits;unsigned int info;unsigned int rate_num;unsigned int rate_den;/* -- SW params -- */struct timespec tstamp_mode;  /* mmap timestamp is updated */unsigned int period_step;unsigned int sleep_min;               /* min ticks to sleep */snd_pcm_uframes_t start_threshold;/** The following two thresholds alleviate playback buffer underruns; when* hw_avail drops below the threshold, the respective action is triggered:*/snd_pcm_uframes_t stop_threshold;     /* - stop playback */snd_pcm_uframes_t silence_threshold;  /* - pre-fill buffer with silence */snd_pcm_uframes_t silence_size;       /* max size of silence pre-fill; when >= boundary,* fill played area with silence immediately */snd_pcm_uframes_t boundary;   /* pointers wrap point *//* internal data of auto-silencer */snd_pcm_uframes_t silence_start; /* starting pointer to silence area */snd_pcm_uframes_t silence_filled; /* size filled with silence */snd_pcm_sync_id_t sync;               /* hardware synchronization ID *//* -- mmap -- */volatile struct snd_pcm_mmap_status *status;volatile struct snd_pcm_mmap_control *control;atomic_t mmap_count;/* -- locking / scheduling -- */spinlock_t lock;wait_queue_head_t sleep;struct timer_list tick_timer;struct fasync_struct *fasync;/* -- private section -- */void *private_data;void (*private_free)(struct snd_pcm_runtime *runtime);/* -- hardware description -- */struct snd_pcm_hardware hw;struct snd_pcm_hw_constraints hw_constraints;/* -- timer -- */unsigned int timer_resolution;        /* timer resolution *//* -- DMA -- */unsigned char *dma_area;      /* DMA area */dma_addr_t dma_addr;          /* physical bus address (not accessible from main CPU) */size_t dma_bytes;             /* size of DMA area */struct snd_dma_buffer *dma_buffer_p;  /* allocated buffer */#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)/* -- OSS things -- */struct snd_pcm_oss_runtime oss;
#endif
};

每个声卡驱动的回调函数,这些数据都是制度的。只有PCM中间层可以修改更新他们。唯一的例外时硬件描述中的DMA buffer信息以及私有数据。此外,如果你使用标准管理buffer分配模式,你不需要设置DMA buffer信息。

硬件描述信息
硬件描述符(结构体snd_pcm_hardware)定义了基础的硬件配置信息。首先,你需要在PCM open的回调函数里面定义它。运行时实例持有的是一份这个描述的拷贝,而不是指针。在open回调函数里面。如果你需要,你可以修改这份拷贝的描述符 (runtime->hw)。比如,在一些芯片模式下,最大声道数支持1,依然可以使用同样的硬件描述符并且在后续的代码中修改channels_max。

struct snd_pcm_runtime *runtime = substream->runtime;
...
runtime->hw = snd_mychip_playback_hw; /* common definition */
if (chip->model == VERY_OLD_ONE)runtime->hw.channels_max = 1;我们的硬件描述符通常是下面这个样子的。
static struct snd_pcm_hardware snd_mychip_playback_hw = {.info = (SNDRV_PCM_INFO_MMAP |SNDRV_PCM_INFO_INTERLEAVED |SNDRV_PCM_INFO_BLOCK_TRANSFER |SNDRV_PCM_INFO_MMAP_VALID),.formats =          SNDRV_PCM_FMTBIT_S16_LE,.rates =            SNDRV_PCM_RATE_8000_48000,.rate_min =         8000,.rate_max =         48000,.channels_min =     2,.channels_max =     2,.buffer_bytes_max = 32768,.period_bytes_min = 4096,.period_bytes_max = 32768,.periods_min =      1,.periods_max =      1024,
};

Info字段保存PCM的类型和能力。<sound/asound.h> 文件中SNDRV_PCM_INFO_XXX类型的宏定义了info字段的类型。是否支持mmap和支持什么交织类型必须指定。SNDRV_PCM_INFO_MMAP 标志为表示驱动支持MMAP,SNDRV_PCM_INFO_INTERLEAVED 标志位表示支持交织PCM,SNDRV_PCM_INFO_NONINTERLEAVED 表示支持非交织PCM。如果交织和非交织都支持,则可以两者都设置。
上面的实例代码中, OSS mmap mode指定了BLOCK_TRANSFER 和MMAP_VALID 。这两个标志位一般都是同时指定的。只有当驱动真正支持mmap的时候才能设置MMAP_VALID 。

SNDRV_PCM_INFO_PAUSE 和SNDRV_PCM_INFO_RESUME表示PCM支持pause/resume。如果设置了这两个标志位,那么在trigger 要做响应的处理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_1035125.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

以柔克刚,以静制动,以弱胜强 举几个生活中通俗易懂的经典案例,说明这些伟大的智慧的精妙之处 省力的竞争策略

简单易懂 很神奇 省力的竞争策略 以柔克刚 案例&#xff1a;柔道运动。柔道是一种利用对手力量的武术&#xff0c;它教导人们如何将对手的力量转化为自己的优势。柔道中的摔法、招式都是以柔克刚的体现&#xff0c;通过巧妙引导而非硬碰硬的对抗&#xff0c;使得体型较小的选…

Open3D(C++) 基于随机抽样与特征值法的点云平面稳健拟合方法

目录 一、算法原理1、论文概述2、参考文献二、代码实现三、结果展示四、测试数据本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的GPT爬虫。 一、算法原理 1、论文概述 针对点云数据含有异常值且传统拟合方法拟合结果不理想的情…

城市治理/县域治理方案:构建基于AI视频能力的“一张网”,助力新型城市发展

TSINGSEE青犀AI算法中台是一款平台型产品&#xff0c;专注于提供各行业中小场景部署解决方案。平台具备接入广、性能强、支持跨平台、芯片国产化等特点&#xff0c;可提供丰富的视图接入能力和智能分析能力。 平台采用了多项IT高新技术&#xff0c;包括视频编解码技术、嵌入式…

linux删除 buff/cache缓存

1.查看当前内存占用 free -h如图&#xff0c;缓存占用了将近9G&#xff0c;接下来进行清理 释放页缓存 echo 1 > /proc/sys/vm/drop_caches释放dentries和inodes echo 2 > /proc/sys/vm/drop_caches释放所有缓存 echo 3 > /proc/sys/vm/drop_caches再次查看&#…

Linux:TCP的流量控制/滑动窗口/延迟应答

文章目录 流量控制滑动窗口延迟应答 本篇要总结的是对于TCP的一些其他概念进行总结 流量控制 接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送,就会造成丢包, 继而引起丢包重传等等一系列连锁反应 因此TCP支持根据…

如何在比特币上验证ZK Proofs

1. 引言 前序博客有&#xff1a; 基于BitVM的乐观 BTC bridgeBitVM&#xff1a;Bitcoin的链下合约Bitcoin Bridge&#xff1a;治愈还是诅咒&#xff1f;BitVM2&#xff1a;比特币上的无需许可验证以比特币脚本来实现SNARK VerifierClementine&#xff1a;Citrea的基于BitVM的…

element-ui tableData导出为xlsx文件

下载 npm i / yarn add file-saver、xlsx库 引入 import FileSaver from “file-saver”; import XLSX from “xlsx”; const simexport (data) > {// if (data.create_time && data.create_time.length > 0) {// data.start_time parseTime(data.create_tim…

JDK,JRE,JVM 区别和联系【大白话Java面试题】

JDK&#xff0c;JRE&#xff0c;JVM 区别和联系 大白话回答&#xff1a; JDK是开发环境一般开发人员需要&#xff0c;包含开发环境&#xff08;JDK)和运行环境&#xff08;JRE&#xff09;&#xff0c;JRE是运行环境&#xff0c;普通用户需要。jre文件夹下的bin文件夹就是JVM的…

搭建电商网站外贸网站用API接口可以实现哪些功能(天猫API接口|京东API接口)

在电商领域&#xff0c;API接口可以实现多种功能&#xff0c;起到连接内外部系统及优化电商业务流程等多种作用&#xff0c;从而来提高电商企业的运营效率。 具体来看&#xff0c;API接口接入可以用来&#xff1a; 商品管理&#xff1a; API接口能够用来获取商品详情等&#…

【原创】基于springboot+vue的会议室预约管理系统

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

AI音乐GPT时刻来临:Suno 快速入门手册!

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

【Go】七、包、init函数、匿名函数、闭包

文章目录 1、包2、本质3、init函数4、匿名函数5、闭包 1、包 不同的包&#xff0c;不同的功能和业务&#xff0c;互相import 关于包&#xff1a; package 包名注意点&#xff1a; 建议包名和所在文件夹同名 main函数一定要放在main包下&#xff0c;否则编译报错 go run&a…

如何划分训练集、测试集、验证集

训练集、测试集和验证集是在机器学习和数据科学中常用的术语&#xff0c;用于评估和验证模型的性能。它们通常用于监督学习任务中。 1. 训练集&#xff08;Training Set&#xff09;&#xff1a;训练集是用于训练机器学习模型的数据集。在训练期间&#xff0c;模型使用训练集中…

若依框架mysql 搜索中文等于不生效

背景&#xff0c;字段存储的是中文 不生效代码如下 <if test"constellation ! null and constellation ! ">AND u.constellation #{constellation}</if> 正确生效的代码如下 <if test"constellation ! null and constellation ! ">A…

瑞吉外卖实战学习--8、人员禁用和启用

前言 1、通过前端页面查看接口 会发现请求方式是put 请求接口是employee 2、检查页面传值 根据浏览器的请求可以看到传值为id和status 2、写put请求&#xff0c;添加修改时间和修改人的id然后传回给后台 /*** 启用和禁用员工账号* param request* param employee* return…

【面试HOT200】数组篇

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招面试coding部分的&#xff0c;整理期间苛求每个算法题目&#xff0c;平衡可读性与代码性能&#xff08;leetcode运行复杂度均打败80%以上&#xff09;。 &#x1f970;来源&#xff1a;材料主要源于…

对【AI技术创业】有哪些机会进行分析和引导

文章目录 方向一&#xff1a;行业解决方案,以下是一些常见的行业解决方案&#xff1a;方向二&#xff1a;智能产品和服务,以下是一些智能产品和服务的示例&#xff1a;方向三&#xff1a;教育和培训 1.智能客户服务&#xff1a; 利用自然语言处理&#xff08;NLP&#xff09;和…

京东云16核64G云服务器租用优惠价格500元1个月,35M带宽

京东云16核64G云服务器租用优惠价格500元1个月、5168元一年&#xff0c;35M带宽&#xff0c;配置为&#xff1a;16C64G-450G SSD系统盘-35M带宽-8000G月流量 华北-北京&#xff0c;京东云活动页面 yunfuwuqiba.com/go/jd 活动链接打开如下图&#xff1a; 京东云16核64G云服务器…

Transformers in Vision:A Survey 阅读笔记

ACM上的一篇综述&#xff0c;讨论Transformer在CV上的应用。 摘要&#xff1a; Among their salient benefits,Transformers enable modeling long dependencies between inputsequence elements and support parallel processing of sequence as compared to recurrent networ…

echarts 地图 自己圈地图 乡镇街道

这个是方式是我实在不愿意做的&#xff01; 如果有现成的最好&#xff0c;没有办法的情况下再用这个东西。 今天公司有一个项目&#xff0c;地方划分了一块区域&#xff0c;但是国家没有审核&#xff0c;但是项目里面用到了一个地图展示数据&#xff01;然后就需要我们自己把…