linux音频子系统
来源:互联网 发布:淘宝代销上架编辑技巧 编辑:程序博客网 时间:2024/06/05 04:00
1.pcm设备
脉冲编码调制(Pulse Code Modulation,PCM),就是把一个时间连续,取值连续的模拟信号变换成时间离散,取值离散的数字信号后在信道中传输,这是基本原理。
根据此原理,在音频领域的数字音频就用pcm设备来代表,pcm也是一种音频格式,可以自定义通道数,采样率,采样精度;我们经常采用的I2S格式其实属于pcm的一种,不过I2S规定了只有2通道。
音频的采样率(rate)一般采用44.1K,16K,48K等,采样精度(format)一般都是8/16/24/32bit
在ALSA框架中,pcm就是控制音频流的,区别于control
2.PCM设备结构体
这部分重要的结构体主要有:
- struct snd_pcm
- struct snd_pcm_str
- struct snd_pcm_substream
这三者的关系可以用下图来表示:
一个音频设备分播放和录音两个功能,对应到pcm就分PLAYBACK和CAPTURE,分别用结构体snd_pcm_str来表示,一个播放或者录音设备可以集成多个音频流,每个音频流用snd_pcm_substream结构体来表示
这三个结构体的逻辑链接关系如下图:
3.pcm设备注册
pcm设备注册函数为:
int snd_pcm_new(struct snd_card *card, const char *id, int device, int playback_count, int capture_count, struct snd_pcm **rpcm){ return _snd_pcm_new(card, id, device, playback_count, capture_count, false, rpcm);}
首先调用_snd_pcm_new来把pcm设备加入到card中,然后card在注册的时候调用pcm的注册函数,把pcm注册到系统中
3.1 创建pcm设备,加入到card中
(sound/core/pcm.c)
static int _snd_pcm_new(struct snd_card *card, const char *id, int device, int playback_count, int capture_count, bool internal, struct snd_pcm **rpcm){ struct snd_pcm *pcm; int err; static struct snd_device_ops ops = { .dev_free = snd_pcm_dev_free, .dev_register = snd_pcm_dev_register,--------------pcm注册函数(card注册时调用) .dev_disconnect = snd_pcm_dev_disconnect, }; if (snd_BUG_ON(!card)) return -ENXIO; if (rpcm) *rpcm = NULL; pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); if (pcm == NULL) { snd_printk(KERN_ERR "Cannot allocate PCM\n"); return -ENOMEM; } pcm->card = card; pcm->device = device; pcm->internal = internal; if (id) strlcpy(pcm->id, id, sizeof(pcm->id)); # snd_pcm_new_stream主要是初始化snd_pcm_substream结构体 if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) { snd_pcm_free(pcm); return err; } if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) { snd_pcm_free(pcm); return err; } mutex_init(&pcm->open_mutex); init_waitqueue_head(&pcm->open_wait); if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) {-----将pcm设备加入到card的devices链表中 snd_pcm_free(pcm); return err; } if (rpcm) *rpcm = pcm; return 0;}
3.2 进行pcm设备的注册
(sound/core/pcm.c)
static int snd_pcm_dev_register(struct snd_device *device){ int cidx, err; struct snd_pcm_substream *substream; struct snd_pcm_notify *notify; char str[16]; struct snd_pcm *pcm; struct device *dev; if (snd_BUG_ON(!device || !device->device_data)) return -ENXIO; pcm = device->device_data; mutex_lock(®ister_mutex); err = snd_pcm_add(pcm); if (err) { mutex_unlock(®ister_mutex); return err; } for (cidx = 0; cidx < 2; cidx++) { int devtype = -1; if (pcm->streams[cidx].substream == NULL || pcm->internal) continue; switch (cidx) { case SNDRV_PCM_STREAM_PLAYBACK:-------------为device命名 sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device); devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; break; case SNDRV_PCM_STREAM_CAPTURE: sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device); devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; break; } /* device pointer to use, pcm->dev takes precedence if * it is assigned, otherwise fall back to card's device * if possible */ dev = pcm->dev; if (!dev) dev = snd_card_get_device_link(pcm->card); /* register pcm */ err = snd_register_device_for_dev(devtype, pcm->card, pcm->device, &snd_pcm_f_ops[cidx],-----pcm设备文件操作函数 pcm, str, dev); if (err < 0) { list_del(&pcm->list); mutex_unlock(®ister_mutex); return err; } snd_add_device_sysfs_file(devtype, pcm->card, pcm->device, &pcm_attrs);-------pcm设备注册 for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) snd_pcm_timer_init(substream); } list_for_each_entry(notify, &snd_pcm_notify_list, list) notify->n_register(pcm); mutex_unlock(®ister_mutex); return 0;}
4.pcm文件操作ops
pcm这块比较难的一点就是这些操作函数了,各种ioctl设置的参数需要对音频技术这块有深入了解,本文就不介绍了,因为core层的东西对于驱动开发来说很少改动(或者基本不改动),遇到问题再解决吧
(sound/core/pcm_native.c)
const struct file_operations snd_pcm_f_ops[2] = { { .owner = THIS_MODULE, .write = snd_pcm_write, .aio_write = snd_pcm_aio_write, .open = snd_pcm_playback_open, .release = snd_pcm_release, .llseek = no_llseek, .poll = snd_pcm_playback_poll, .unlocked_ioctl = snd_pcm_playback_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, .fasync = snd_pcm_fasync, .get_unmapped_area = snd_pcm_get_unmapped_area, }, { .owner = THIS_MODULE, .read = snd_pcm_read, .aio_read = snd_pcm_aio_read, .open = snd_pcm_capture_open, .release = snd_pcm_release, .llseek = no_llseek, .poll = snd_pcm_capture_poll, .unlocked_ioctl = snd_pcm_capture_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, .fasync = snd_pcm_fasync, .get_unmapped_area = snd_pcm_get_unmapped_area, }};
4.1 open函数
snd_pcm_capture_open和snd_pcm_playback_open函数最后都会调用snd_pcm_open,这里不详细介绍open的过程了,只介绍下这边引出来的另一个结构体:snd_pcm_runtime
这个结构体只是在运行的时候会动态创建,具体可以参考函数snd_pcm_attach_substream
此结构体主要是设置各种参数,保存运行时的状态等
struct snd_pcm_runtime { /* -- Status -- */ struct snd_pcm_substream *trigger_master; struct timespec 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 */ unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */ unsigned long hw_ptr_buffer_jiffies; /* buffer time in jiffies */ snd_pcm_sframes_t delay; /* extra delay; typically FIFO size */ u64 hw_ptr_wrap; /* offset for hw_ptr due to boundary wrap-around */ /* -- 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 */ 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; unsigned int no_period_wakeup: 1; /* -- SW params -- */ int tstamp_mode; /* mmap timestamp is updated */ unsigned int period_step; snd_pcm_uframes_t start_threshold; snd_pcm_uframes_t stop_threshold; snd_pcm_uframes_t silence_threshold; /* Silence filling happens when noise is nearest than this */ snd_pcm_uframes_t silence_size; /* Silence filling size */ snd_pcm_uframes_t boundary; /* pointers wrap point */ snd_pcm_uframes_t silence_start; /* starting pointer to silence area */ snd_pcm_uframes_t silence_filled; /* size filled with silence */ union snd_pcm_sync_id sync; /* hardware synchronization ID */ /* -- mmap -- */ struct snd_pcm_mmap_status *status; struct snd_pcm_mmap_control *control; /* -- locking / scheduling -- */ snd_pcm_uframes_t twake; /* do transfer (!poll) wakeup if non-zero */ wait_queue_head_t sleep; /* poll sleep */ wait_queue_head_t tsleep; /* transfer sleep */ 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; /* -- interrupt callbacks -- */ void (*transfer_ack_begin)(struct snd_pcm_substream *substream); void (*transfer_ack_end)(struct snd_pcm_substream *substream); /* -- timer -- */ unsigned int timer_resolution; /* timer resolution */ int tstamp_type; /* timestamp type */ /* -- 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#ifdef CONFIG_SND_PCM_XRUN_DEBUG struct snd_pcm_hwptr_log *hwptr_log;#endif};
5.change log
- linux音频子系统
- linux音频子系统
- linux音频子系统
- linux音频子系统
- Linux ALSA音频框架分析二:linux音频子系统介绍
- linux 音频子系统学习及软件安装
- Linux内核工程导论——硬件:音频子系统
- Linux音频子系统-转自droidphone的CSDN博客
- Linux内核架构 Linux设备驱动 Linux电源管理 Linux音频子系统 Linux中断子系统 Linux时间管理系统 Linux输入子系统
- Android音频子系统,音频流(六)
- Android 音频子系统简要介绍
- linux驱动由浅入深系列:tinyalsa(tinymix/tinycap/tinyplay/tinypcminfo)音频子系统之一
- Linux子系统
- Linux子系统
- Linux子系统系列-时钟子系统
- Linux 输入子系统 input 子系统
- android 音频子系统框架(一)
- android 音频子系统-AudioFlinger(二)
- linux内核tasklet机制
- java中的代码块和java的继承关系
- ccf 通信问题 连通图
- Mezzanine多site管理问题
- Linux文件管理 (实验3)
- linux音频子系统
- Spark算子[05]:parallelize,makeRDD,textFile
- mobx干货资料汇总
- MySQL锁的用法之行级锁
- C# 抽象类
- Mezzanine怎样为BLOG创建分级目录
- Html5基本标签的使用
- 多选框全选中,全选框自动选中
- PAT 1030. 完美数列(25)