https://www.alsa-project.org/alsa-doc/alsa-lib/pcm_external_plugins.html

External Plugin: I/O Plugin

The I/O-type plugin is a PCM plugin to work as the input or output terminal point, i.e. as a user-space PCM driver.

The new plugin is created via snd_pcm_ioplug_create() function. The first argument is a pointer of the pluging information. Some of this struct must be initialized in prior to call snd_pcm_ioplug_create(). Then the function fills other fields in return. The rest arguments, name, stream and mode, are usually identical with the values passed from the ALSA plugin constructor.

The following fields are mandatory: version, name, callback. Otherfields are optional and should be initialized with zero.

The constant SND_PCM_IOPLUG_VERSION must be passed to the version field for the version check in alsa-lib. A non-NULL ASCII string has to be passed to the name field. The callback field contains the table of callback functions for this plugin (defined as snd_pcm_ioplug_callback_t).

flags field specifies the optional bit-flags. poll_fd and poll_events specify the poll file descriptor and the corresponding poll events (POLLIN, POLLOUT) for the plugin. If the plugin requires multiple poll descriptors or poll descriptor(s) dynamically varying, set poll_descriptors and poll_descriptors_count callbacks to the callback table. Then the poll_fd and poll_events field are ignored.

mmap_rw specifies whether the plugin behaves in the pseudo mmap mode. When this value is set to 1, the plugin creates always a local buffer and performs read/write calls using this buffer as if it's mmapped. The address of local buffer can be obtained via snd_pcm_ioplug_mmap_areas() function. When poll_fd, poll_events and mmap_rw fields are changed after snd_pcm_ioplug_create(), call snd_pcm_ioplug_reinit_status() to reflect the changes.

The driver can set an arbitrary value (pointer) to private_data field to refer its own data in the callbacks.

The rest fields are filled by snd_pcm_ioplug_create(). The pcm field is the resultant PCM handle. The others are the current status of the PCM.

The callback functions in snd_pcm_ioplug_callback_t define the real behavior of the driver. At least, start, stop and pointer callbacks must be given. Other callbacks are optional. The start and stop callbacks are called when the PCM stream is started and stopped, repsectively. The pointer callback returns the current DMA position, which may be called at any time.

The transfer callback is called when any data transfer happens. It receives the area array, offset and the size to transfer. The area array contains the array ofsnd_pcm_channel_area_t with the elements of number of channels.

When the PCM is closed, close callback is called. If the driver allocates any internal buffers, they should be released in this callback. The hw_params and hw_free callbacks are called when hw_params are set and reset, respectively. Note that they may be called multiple times according to the application. Similarly, sw_params callback is called when sw_params is set or changed.

The prepare, drain, pause and resume callbacks are called when snd_pcm_prepare()snd_pcm_drain()snd_pcm_pause(), and snd_pcm_resume() are called. The poll_descriptors_count and poll_descriptors callbacks are used to return the multiple or dynamic poll descriptors as mentioned above. The poll_revents callback is used to modify poll events. If the driver needs to mangle the native poll events to proper poll events for PCM, you can do it in this callback.

Finally, the dump callback is used to print the status of the plugin.

Note that some callbacks (start, stop, pointer, transfer and pause) may be called inside the internal pthread mutex, and they shouldn't call the PCM functions again unnecessarily from the callback itself; otherwise it may lead to a deadlock.

The hw_params constraints can be defined via either snd_pcm_ioplug_set_param_minmax() and snd_pcm_ioplug_set_param_list() functions after callingsnd_pcm_ioplug_create(). The former defines the minimal and maximal acceptable values for the given hw_params parameter (SND_PCM_IOPLUG_HW_XXX). This function can't be used for the format parameter. The latter function specifies the available parameter values as the list.

To clear the parameter constraints, call snd_pcm_ioplug_params_reset() function.

代码参考pcm_ac52.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <alsa/asoundlib.h>
#include <alsa/pcm_external.h>
#include <alsa/pcm_plugin.h> #define MIN_PERIOD 256
#define MAX_PERIOD 1024
#define MIN_CHANNELS 1
#define MAX_CHANNELS 6 struct fellow_ctx {
snd_pcm_ioplug_t io;
snd_pcm_t *slave;
snd_pcm_format_t format;
unsigned int channels;
unsigned int rate;
unsigned int frame_size;
char *inbuf;
char *outbuf;
int outbuf_size;
snd_pcm_uframes_t transfer;
int remain;
int filled;
unsigned int slave_period_size;
unsigned int slave_buffer_size;
snd_pcm_hw_params_t *hw_params;
FILE *dump_fp;
}; static void convert_data(struct fellow_ctx *rec)
{
int bytes_per_frame = (snd_pcm_format_width(rec->format) >> ) * rec->channels;
memcpy(rec->outbuf, rec->inbuf, rec->frame_size * bytes_per_frame);
rec->remain = rec->outbuf_size / bytes_per_frame;
rec->filled = ;
} static int write_out(snd_pcm_ioplug_t *io, struct fellow_ctx *rec)
{
int err, ofs = ;
int bytes_per_frame = (snd_pcm_format_width(rec->format) >> ) * io->channels;
if (!rec->remain)
return ;
while (rec->remain) {
err = snd_pcm_writei(rec->slave, rec->outbuf + ofs, rec->remain);
if (err < ) {
if (err == -EPIPE)
io->state = SND_PCM_STATE_XRUN;
return err;
} else if (!err)
break;
if (rec->dump_fp)
fwrite(rec->outbuf + ofs, , err * bytes_per_frame, rec->dump_fp);
if (err < rec->remain)
ofs += (rec->remain - err) * bytes_per_frame;
rec->remain -= err;
}
if (rec->remain && ofs)
memmove(rec->outbuf, rec->outbuf + ofs, rec->remain * bytes_per_frame);
return ;
} static int fellow_drain(snd_pcm_ioplug_t *io)
{
struct fellow_ctx *rec = io->private_data;
int bytes_per_frame = (snd_pcm_format_width(rec->format) >> ) * rec->channels;
int err;
if (rec->filled) {
if ((err = write_out(io, rec)) < )
return err;
memset(rec->inbuf + rec->filled * bytes_per_frame, , (rec->frame_size - rec->filled) * bytes_per_frame);
convert_data(rec);
}
err = write_out(io, rec);
if (err < )
return err;
return snd_pcm_drain(rec->slave);
} static int fill_data(snd_pcm_ioplug_t *io, const snd_pcm_channel_area_t *areas, unsigned int offset, unsigned int size)
{
struct fellow_ctx *rec = io->private_data;
int bytes_per_frame = (snd_pcm_format_width(rec->format) >> ) * io->channels;
unsigned int len = rec->frame_size - rec->filled;
char *dst;
int err; if ((err = write_out(io, rec)) < )
return err;
if (size > len)
size = len;
dst = rec->inbuf + rec->filled * bytes_per_frame;
memcpy(dst , areas->addr + offset * bytes_per_frame, size * bytes_per_frame);
rec->filled += size;
if (rec->filled == rec->frame_size) {
convert_data(rec);
write_out(io, rec);
}
return (int)size;
} static snd_pcm_sframes_t fellow_transfer(snd_pcm_ioplug_t *io, const snd_pcm_channel_area_t *areas, snd_pcm_uframes_t offset, snd_pcm_uframes_t size)
{
struct fellow_ctx *rec = io->private_data;
snd_pcm_sframes_t result = ;
int err = ; do {
err = fill_data(io, areas, offset, size);
if (err < )
break;
offset += (unsigned int)err;
size -= (unsigned int)err;
result += err;
rec->transfer += err;
} while (size);
return result > ? result : err;
} static snd_pcm_sframes_t fellow_pointer(snd_pcm_ioplug_t *io)
{
struct fellow_ctx *rec = io->private_data;
snd_pcm_sframes_t delay = ;
snd_pcm_state_t state;
int err; state = snd_pcm_state(rec->slave);
switch (state) {
case SND_PCM_STATE_RUNNING:
case SND_PCM_STATE_DRAINING:
if ((err = snd_pcm_delay(rec->slave, &delay)) < )
return err;
break;
case SND_PCM_STATE_XRUN:
case SND_PCM_STATE_SUSPENDED:
return -EPIPE;
default:
return ;
} if (delay < || delay >= (snd_pcm_sframes_t) rec->slave_buffer_size)
delay = ;
delay = (snd_pcm_sframes_t)io->appl_ptr - delay;
if (delay < ) {
delay += io->buffer_size;
if (delay < )
delay = ;
}
delay %= io->buffer_size;
return delay;
} static int fellow_slave_hw_params_half(struct fellow_ctx *rec)
{
int err;
if ((err = snd_pcm_hw_params_malloc(&rec->hw_params)) < )
return err;
if ((err = snd_pcm_hw_params_any(rec->slave, rec->hw_params)) < ) {
SNDERR("Cannot get slave hw_params");
goto out;
}
if ((err = snd_pcm_hw_params_set_access(rec->slave, rec->hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < ) {
SNDERR("Cannot set slave access");
goto out;
}
if ((err = snd_pcm_hw_params_set_channels(rec->slave, rec->hw_params, )) < ) {
SNDERR("Cannot set slave channels 2");
goto out;
}
if ((err = snd_pcm_hw_params_set_format(rec->slave, rec->hw_params, rec->format)) < ) {
SNDERR("Cannot set slave format");
goto out;
}
if ((err = snd_pcm_hw_params_set_rate(rec->slave, rec->hw_params, rec->rate, )) < ) {
SNDERR("Cannot set slave rate:%d", rec->rate);
goto out;
}
return ;
out:
free(rec->hw_params);
rec->hw_params = NULL;
return err;
} static int fellow_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params ATTRIBUTE_UNUSED)
{
struct fellow_ctx *rec = io->private_data;
snd_pcm_uframes_t period_size, buffer_size;
int err;
rec->format = io->format;
rec->rate = io->rate;
rec->channels = io->channels;
if (!rec->hw_params) {
err = fellow_slave_hw_params_half(rec);
if (err < )
return err;
}
period_size = io->period_size;
if ((err = snd_pcm_hw_params_set_period_size_near(rec->slave, rec->hw_params, &period_size, NULL)) < ) {
SNDERR("Cannot set slave period size:%ld", period_size);
return err;
}
buffer_size = io->buffer_size;
if ((err = snd_pcm_hw_params_set_buffer_size_near(rec->slave, rec->hw_params, &buffer_size)) < ) {
SNDERR("Cannot set slave buffer size:%ld", buffer_size);
return err;
}
if ((err = snd_pcm_hw_params(rec->slave, rec->hw_params)) < ) {
SNDERR("Cannot set slave hw_params");
return err;
}
rec->slave_period_size = period_size;
rec->slave_buffer_size = buffer_size;
rec->frame_size = io->period_size * ;
return ;
} static int fellow_hw_free(snd_pcm_ioplug_t *io)
{
struct fellow_ctx *rec = io->private_data;
if (rec->hw_params) {
free(rec->hw_params);
rec->hw_params = NULL;
}
if (rec->dump_fp)
fclose(rec->dump_fp);
return snd_pcm_hw_free(rec->slave);
} static int fellow_sw_params(snd_pcm_ioplug_t *io, snd_pcm_sw_params_t *params)
{
struct fellow_ctx *rec = io->private_data;
snd_pcm_sw_params_t *sparams;
snd_pcm_uframes_t avail_min, start_threshold;
int len;
snd_pcm_sw_params_get_avail_min(params, &avail_min);
snd_pcm_sw_params_get_start_threshold(params, &start_threshold); len = avail_min;
len += (int)rec->slave_buffer_size - (int)io->buffer_size;
if (len < )
avail_min = ;
else
avail_min = len;
snd_pcm_sw_params_alloca(&sparams);
snd_pcm_sw_params_current(rec->slave, sparams);
snd_pcm_sw_params_set_avail_min(rec->slave, sparams, avail_min);
snd_pcm_sw_params_set_start_threshold(rec->slave, sparams, start_threshold);
return snd_pcm_sw_params(rec->slave, sparams);
} static int fellow_stop(snd_pcm_ioplug_t *io)
{
struct fellow_ctx *rec = io->private_data;
return snd_pcm_drop(rec->slave);
} static int fellow_start(snd_pcm_ioplug_t *io)
{
struct fellow_ctx *rec = io->private_data;
if (snd_pcm_state(rec->slave) == SND_PCM_STATE_RUNNING)
return ;
return snd_pcm_start(rec->slave);
} static void fellow_free(struct fellow_ctx *rec)
{
if (rec->inbuf) {
free(rec->inbuf);
rec->inbuf = NULL;
}
if (rec->outbuf) {
free(rec->outbuf);
rec->outbuf = NULL;
}
} static int alloc_buffer(snd_pcm_ioplug_t *io)
{
struct fellow_ctx *rec = io->private_data;
int bytes_per_frame = (snd_pcm_format_width(rec->format) >> ) * io->channels;
unsigned int buf_size = rec->frame_size * bytes_per_frame;
rec->inbuf = malloc(buf_size);
if (!rec->inbuf)
return -ENOMEM;
rec->outbuf_size = buf_size;
rec->outbuf = malloc(buf_size);
if (!rec->outbuf)
return -ENOMEM;
return ;
} static int fellow_prepare(snd_pcm_ioplug_t *io)
{
struct fellow_ctx *rec = io->private_data; fellow_free(rec);
if (alloc_buffer(io) < )
return -ENOMEM;
rec->transfer = ;
rec->remain= ;
rec->filled= ; return snd_pcm_prepare(rec->slave);
} static int fellow_close(snd_pcm_ioplug_t *io)
{
struct fellow_ctx *rec = io->private_data;
snd_pcm_t *slave = rec->slave;
fellow_free(rec);
if (slave) {
rec->slave = NULL;
return snd_pcm_close(slave);
} return ;
} static snd_pcm_ioplug_callback_t fellow_ops = {
.start = fellow_start,
.stop = fellow_stop,
.pointer = fellow_pointer,
.transfer = fellow_transfer,
.close = fellow_close,
.hw_params = fellow_hw_params,
.hw_free = fellow_hw_free,
.sw_params = fellow_sw_params,
.prepare= fellow_prepare,
.drain = fellow_drain,
}; #define ARRAY_SIZE(ary) (sizeof(ary)/sizeof(ary[0])) static int fellow_set_hw_constraint(struct fellow_ctx *rec)
{
static unsigned int accesses[] = {
SND_PCM_ACCESS_RW_INTERLEAVED,
SND_PCM_ACCESS_RW_NONINTERLEAVED
};
unsigned int formats[] = {SND_PCM_FORMAT_S16};
unsigned int rates[] = {, };
int err; err = snd_pcm_ioplug_set_param_list(&rec->io, SND_PCM_IOPLUG_HW_ACCESS, ARRAY_SIZE(accesses), accesses);
if (err < )
return err;
if ((err = snd_pcm_ioplug_set_param_list(&rec->io, SND_PCM_IOPLUG_HW_FORMAT, ARRAY_SIZE(formats), formats)) <
|| (err = snd_pcm_ioplug_set_param_list(&rec->io, SND_PCM_IOPLUG_HW_RATE, ARRAY_SIZE(rates), rates)) <
|| (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_CHANNELS, MIN_CHANNELS, MAX_CHANNELS)) < )
return err; if ((err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, MIN_PERIOD * * MIN_CHANNELS, MAX_PERIOD * * MAX_CHANNELS)) <
|| (err = snd_pcm_ioplug_set_param_minmax(&rec->io, SND_PCM_IOPLUG_HW_PERIODS, , )) < )
return err;
return ;
} SND_PCM_PLUGIN_DEFINE_FUNC(fellow)
{
snd_config_iterator_t i, next;
int err;
const char *pcm_string = NULL;
const char *dump_file = NULL;
char devstr[] = "default";
struct fellow_ctx *rec;
if (stream != SND_PCM_STREAM_PLAYBACK)
return -EINVAL;
snd_config_for_each(i, next, conf) {
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(n, &id) < )
continue;
if (strcmp(id, "comment") == || strcmp(id, "type") == )
continue;
if (strcmp(id, "slavepcm") == ) {
if (snd_config_get_string(n, &pcm_string) < ) {
SNDERR("slavepcm must be string");
return -EINVAL;
}
continue;
}
if (strcmp(id, "dumpfile") == ) {
if (snd_config_get_string(n, &dump_file) < ) {
SNDERR("dumpfile must be string");
return -EINVAL;
}
continue;
}
SNDERR("Unknown field %s", id);
return -EINVAL;
}
rec = calloc(, sizeof(*rec));
if (!rec)
return -ENOMEM;
if (!pcm_string || pcm_string[0] == '\0')
err = snd_pcm_open(&rec->slave, devstr, stream, mode);
else
err = snd_pcm_open(&rec->slave, pcm_string, stream, mode);
if (err < )
goto error;
if (!dump_file || dump_file[] == '\0')
rec->dump_fp = NULL;
else
rec->dump_fp = fopen(dump_file, "wb"); rec->io.version = SND_PCM_IOPLUG_VERSION;
rec->io.name = "Fellow Plugin";
rec->io.mmap_rw = ;
rec->io.callback = &fellow_ops;
rec->io.private_data = rec;
err = snd_pcm_ioplug_create(&rec->io, name, stream, mode);
if (err < ) {
goto error;
}
err = fellow_set_hw_constraint(rec);
if (err < ) {
snd_pcm_ioplug_delete(&rec->io);
goto error;
} *pcmp = rec->io.pcm;
return ;
error:
if (rec->slave)
snd_pcm_close(rec->slave);
free(rec);
return err;
}
SND_PCM_PLUGIN_SYMBOL(fellow);

Makefile:

ALSA_INC_PATH=/home/fellow/alsa-lib-1.2./output/usr/include
ALSA_LIB_PATH=/usr/lib/i386-linux-gnu export LD_LIBRARY_PATH=${ALSA_LIB_PATH}:$LD_LIBRARY_PATH
export CC=gcc
export CFLAGS=-I${ALSA_INC_PATH}
export LDFLAGS=-L{ALSA_LIB_PATH} -lasound
SOURCE=pcm_fellow.c
TARGET=libasound_module_pcm_fellow.so
all:
${CC} ${SOURCE} ${CFLAGS} ${LDFLAGS} -shared -fPIC -DPIC -Wall -Werror -o ${TARGET}

将libasound_module_pcm_fellow.so copy到/usr/lib/alsa-lib;并将/usr/lib/alsa-lib加到/etc/ld.config,否则有可能在运行时,找不到so

在home目录下新建.asoundrc

pcm.myout {
type fellow
slavepcm "default"
dumpfile "fellowdump.pcm"
}

执行命令:aplay -D myout xxx.wav

ALSA lib-io plugin的更多相关文章

  1. ALSA lib编译

    http://blog.sina.com.cn/s/blog_7d7e9d0f0101lqlp.html alsa  lib: #!bin/sh rm -rf ./output/* mkdir -p ...

  2. ALSA lib基本概念

    1.channel 通道,即我们熟知的声道数.左/右声道,5.1channel等等 2.sample A sample is a single value that describes the amp ...

  3. ALSA lib调用实例

    1. Display Some PCM Types and Formats 2. Opening PCM Device and Setting Parameters /* This example o ...

  4. ALSA 学习小记

    对于playback snd_pcm_begin snd_pcm_commit, 貌似 commit给的frame才会使得alsa去把数据填充 转自 http://magodo.github.io/ ...

  5. 基于Maven管理的Mapreduce程序下载依赖包到LIB目录

    1.Mapreduce程序需要打包作为作业提交到Hadoop集群环境运行,但是程序中有相关的依赖包,如果没有一起打包,会出现xxxxClass Not Found . 2.在pom.xml文件< ...

  6. Xamarin+Prism开发详解七:Plugin开发与打包测试

    有了上章[Xamarin+Prism开发详解六:DependencyService与IPlatformInitializer的关系]的基础,现在来理解Plugin开发就简单了. 本文实例代码地址:ht ...

  7. druid.io本地集群搭建 / 扩展集群搭建

    druid.io 是一个比较重型的数据库查询系统,分为5种节点 . 在此就不对数据库进行介绍了,如果有疑问请参考白皮书: http://pan.baidu.com/s/1eSFlIJS 单台机器的集群 ...

  8. Druid.io启用SQL支持

    Druid.io的SQL功能虽然在试验阶段,但是也支持了大部分的功能,而且还可以通过 Avatica JDBC查看请求的json,有助于我们理解Druid.io的语法.Druid.io有个比较坑的是, ...

  9. linux alsa pcm(此pcm非硬件pcm接口)

    转:https://blog.csdn.net/crycheng/article/details/7095899 CODEC :音频芯片的控制,比如静音.打开(关闭)ADC(DAC).设置ADC(DA ...

  10. 关于Mysql Enterprise Audit plugin的使用

    正如之前看到的一篇文章,假设想要知道是谁登陆了你的数据库server,干了什么东西,那么你须要使用Mysql Enterprise Audit plugin. 以下介绍一下Mysql Enterpri ...

随机推荐

  1. PMP--1.6 项目经理

    本节都是理论的东西,可以在管理没有思路的或者管理陷入困境的时候当做提升或解决问题的思路来看. 一.项目经理 1. 项目经理.职能经理与运营经理的区别 (1)职能经理专注于对某个职能领域或业务部门的管理 ...

  2. Python基础知识总结笔记(四)函数

    Python基础知识总结笔记(四)函数python中的函数函数中的参数变量作用域偏函数PFA递归函数高阶函数BIFs中的高阶函数匿名函数lambda闭包Closure装饰器Decorator函数式编程 ...

  3. TCP/IP详解阅读记录----第一章 概述

    1.TCP/IP协议族中不同层次的协议 2.五类互联网地址 3.各类IP地址范围 4.数据进入协议栈时的封装过程 5.以太网数据帧的分用过程

  4. css 隐藏滚动条

    如何使用css隐藏滚动条 如何隐藏滚动条,同时仍然可以在任何元素上滚动? 首先,如果需要隐藏滚动条并在内容溢出时显示滚动条,只需要设置overflow:auto样式即可.想要完全隐藏滚动条只需设置ov ...

  5. zabbix | 离线安装agent

    zabbix | 离线安装agent 环境 centos6.7 zabbix-server 3.4 步骤 1. 下载rpm包 首先下载支持的插件 yum install yum-plugin-down ...

  6. 回炉重造之重读Windows核心编程-006-线程

    线程也是有两部分组成的: 线程的内核对象,操作系统用来管理线程和统计线程信息的地方. 线程堆栈,用于维护现场在执行代码的时候用到的所有函数参数和局部变量. 进程是线程的容器,如果进程中有一个以上的线程 ...

  7. Jupyter Notebook 常用快捷键 (转)

    Jupyter Notebook 有两种键盘输入模式. 编辑模式,允许你往单元中键入代码或文本:这时的单元框线是绿色的. 命令模式,键盘输入运行程序命令:这时的单元框线为蓝色. 命令模式 (按键 Es ...

  8. 将String类型的json数据转换为真正的json数据

    问题 在做JavaWeb项目的时候,我们经常需要将Java对象转化为Json数据格式响应到前台页面,但是转化完成之后,看着是Json类型的数据格式,但实际上是字符串类型,在这里说两个方法将String ...

  9. 【终端命令】SSH服务,远程登录

    一.SSH协议 在Linux中SSH是非常常用的工具,通过SSH客户端我们可以连接到运行了SSH服务器的远程机器上. SSH客户端是一种 使用"Secure Shell (SSH)" ...

  10. Python 文件&异常 初学者笔记

    文件 读取整个文件 with open('pi_30_digits.txt') as file_object :#Python在当前执行文件目录寻找指定文件#filename = 文件的绝对路径或相对 ...