alsa的音频架构

2 篇文章 0 订阅

What’s alsa

ALSA是Advanced Linux Sound Architecture的缩写,高级Linux声音架构的简称,它在Linux操作系统上提供了音频和MIDI(Musical Instrument Digital Interface,音乐设备数字化接口)的支持。

alsa 代码分析

snd_pcm_open
snd_pcm_start
snd_pcm_writei
snd_pcm_readi
snd_pcm_pause
snd_pcm_close

如何选定snd_pcm_fast_ops_t

在这里插入图片描述
从_snd_pcm_writei函数对应的ops上来看,open时会指定fast_ops来代表不同的声卡,那么如何选定这个声卡以及驱动呢?

Samples:
pcm.plughw {
	@args [ CARD DEV SUBDEV ]
	@args.CARD {
		type string
		default {
			@func getenv
			vars [
				ALSA_PCM_CARD
				ALSA_CARD
			]
			default {
				@func refer
				name defaults.pcm.card
			}
		}
	}
	@args.DEV {
		type integer
		default {
			@func igetenv
			vars [
				ALSA_PCM_DEVICE
			]
			default {
				@func refer
				name defaults.pcm.device
			}
		}
	}
	@args.SUBDEV {
		type integer
		default {
			@func refer
			name defaults.pcm.subdevice
		}
	}		
	type plug
	slave.pcm {
		type hw
		card $CARD
		device $DEV
		subdevice $SUBDEV
	}
	hint {
		show {
			@func refer
			name defaults.namehint.extended
		}
		description "Hardware device with all software conversions"
	}
}

    const char *name = "plughw:0,0";
        int ret = snd_pcm_open(&handle, name, stream, mode);
static int snd_pcm_open_noupdate(snd_pcm_t **pcmp, snd_config_t *root,
				 const char *name, snd_pcm_stream_t stream,
				 int mode, int hop)
	int snd_config_search_definition(snd_config_t *config,
				 const char *base, const char *name,
				 snd_config_t **result)
			if (args) {
		args++;
		key = alloca(args - name);
		memcpy(key, name, args - name - 1);	//这里得到key = plughw
		key[args - name - 1] = '\0';
	} else {
		key = (char *) name;
	}

	err = snd_config_search(pcm_conf, "type", &conf);  //得到 type = plug
	if (err < 0) {
		SNDERR("type is not defined");
		return err;
	}
			open_name = buf;
			sprintf(buf, "_snd_pcm_%s_open", str);
得到函数名为:
_snd_pcm_plug_open

//dlopen/dlsym加载函数名,并调用函数
	open_func = snd_dlobj_cache_get(lib, open_name,
			SND_DLSYM_VERSION(SND_PCM_DLSYM_VERSION), 1);
	if (open_func) {
		err = open_func(pcmp, name, pcm_root, pcm_conf, stream, mode);			

int _snd_pcm_plug_open(snd_pcm_t **pcmp, const char *name,
		       snd_config_t *root, snd_config_t *conf, 
		       snd_pcm_stream_t stream, int mode)


	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
	pcm->fast_ops = slave->fast_ops;

int snd_pcm_open_named_slave(snd_pcm_t **pcmp, const char *name,
			     snd_config_t *root,
			     snd_config_t *conf, snd_pcm_stream_t stream,
			     int mode, snd_config_t *parent_conf)

根据slave的type:hw,再找到
	slave.pcm {
		type hw
		card $CARD
		device $DEV
		subdevice $SUBDEV
	}
int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
		     snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf,
		     snd_pcm_stream_t stream, int mode)
{

int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, int fd,
		       int sync_ptr_ioctl)

最早找到fast_ops对应这个struct
	pcm->fast_ops = &snd_pcm_hw_fast_ops;

write 函数

        r = snd_pcm_writei(handle, pData, count);  =>
static inline snd_pcm_sframes_t _snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
{
	/* lock handled in the callback */
	if (!pcm->fast_ops->writei)
		return -ENOSYS;
	return pcm->fast_ops->writei(pcm->fast_op_arg, buffer, size);
}
=>
static snd_pcm_sframes_t snd_pcm_hw_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)


=> 写入驱动当中
	int err;
	snd_pcm_hw_t *hw = pcm->private_data;
	int fd = hw->fd;
	struct snd_xferi xferi;
	xferi.buf = (char*) buffer;
	xferi.frames = size;
	xferi.result = 0; /* make valgrind happy */
	if (ioctl(fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xferi) < 0)
		err = -errno;
	else
		err = query_status_and_control_data(hw);

驱动

kernel/sound/core/pcm_native.c:2833:
case SNDRV_PCM_IOCTL_WRITEI_FRAMES:

./core/pcm_lib.c:2127:snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size)

2127 snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size)

./core/pcm_lib.c:2011:static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,

通过transfer接口把数据发送到USB声卡
snd_pcm_stream_unlock_irq(substream);
err = transfer(substream, appl_ofs, data, offset, frames);
snd_pcm_stream_lock_irq(substream);

mmap的方式写入PCM数据

最终ALSA是靠mmap的方式将数据写入到驱动

snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
				      snd_pcm_uframes_t offset, snd_pcm_uframes_t size,
				      snd_pcm_xfer_areas_func_t func)
{
	snd_pcm_uframes_t xfer = 0;
	snd_pcm_sframes_t err = 0;
	snd_pcm_state_t state;

参考

https://www.cnblogs.com/wen123456/p/14043087.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值