概述
逻辑设备篇
转自:https://me.csdn.net/zyuanyun
Linux ALSA 音频系统:逻辑设备篇
Linux ALSA 音频系统:逻辑设备篇
6. 声卡和 PCM 设备的建立过程
前面几章分析了 Codec、Platform、Machine 驱动的组成部分及其注册过程,这三者都是物理设备相关的,大家应该对音频物理链路有了一定的认知。接着分析音频驱动的中间层,由于这些并不是真正的物理设备,故我们称之为逻辑设备。
PCM 逻辑设备,我们又习惯称之为 PCM 中间层或 pcm native,起着承上启下的作用:往上是与用户态接口的交互,实现音频数据在用户态和内核态之间的拷贝;往下是触发 codec、platform、machine 的操作函数,实现音频数据在 dma_buffer <-> cpu_dai <-> codec
之间的传输。后面章节将会详细分析这个过程,这里还是先从声卡的注册谈起。
//
// 声明:本文由 http://blog.csdn.net/zyuanyun 原创,转载请注明出处,谢谢!
//
声卡驱动中,一般挂载着多个逻辑设备,看看我们计算机的声卡驱动有几个逻辑设备:
$ cat /proc/asound/devices
1: : sequencer
2: [ 0- 7]: digital audio playback
3: [ 0- 3]: digital audio playback
4: [ 0- 2]: digital audio capture
5: [ 0- 0]: digital audio playback
6: [ 0- 0]: digital audio capture
7: [ 0- 3]: hardware dependent
8: [ 0- 0]: hardware dependent
9: [ 0] : control
33: : timer
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
Device | Description |
---|---|
digital audio playback | 用于回放的 PCM 设备 |
digital audio capture | 用于录制的 PCM 设备 |
control | 用于声卡控制的 CTL 设备,如通路控制、音量调整等 |
timer | 定时器设备 |
sequencer | 音序器设备 |
嵌入式系统中,通常我们更关心 PCM 和 CTL 这两种设备。
设备节点如下:
$ ll /dev/snd
drwxr-xr-x 3 root root 260 Feb 26 13:59 ./
drwxr-xr-x 16 root root 4300 Mar 6 17:07 ../
drwxr-xr-x 2 root root 60 Feb 26 13:59 by-path/
crw-rw---T+ 1 root audio 116, 9 Feb 26 13:59 controlC0
crw-rw---T+ 1 root audio 116, 8 Feb 26 13:59 hwC0D0
crw-rw---T+ 1 root audio 116, 7 Feb 26 13:59 hwC0D3
crw-rw---T+ 1 root audio 116, 6 Feb 26 13:59 pcmC0D0c
crw-rw---T+ 1 root audio 116, 5 Mar 6 19:08 pcmC0D0p
crw-rw---T+ 1 root audio 116, 4 Feb 26 13:59 pcmC0D2c
crw-rw---T+ 1 root audio 116, 3 Feb 26 13:59 pcmC0D3p
crw-rw---T+ 1 root audio 116, 2 Feb 26 13:59 pcmC0D7p
crw-rw---T+ 1 root audio 116, 1 Feb 26 13:59 seq
crw-rw---T+ 1 root audio 116, 33 Feb 26 13:59 timer
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
可以看到这些设备节点的 Major=116,Minor 则与 /proc/asound/devices
所列的对应起来,都是字符设备。上层可以通过 open/close/read/write/ioctl
等系统调用来操作声卡设备,这和其他字符设备类似,但一般情况下我们会使用已封装好的用户接口库如 tinyalsa、alsa-lib。
6.1. 声卡结构概述
回顾下 ASoC 是如何注册声卡的,详细请参考章节 5. ASoC machine driver
,这里仅简单陈述下:
- Machine 驱动初始化时,
.name = "soc-audio"
的 platform_device 与 platform_driver 匹配成功,触发soc_probe()
调用; - 继而调用
snd_soc_register_card()
:- 为每个音频物理链路找到对应的 codec、codec_dai、cpu_dai、platform 设备实例,完成 dai_link 的绑定;
- 调用
snd_card_create()
创建声卡; - 依次回调 cpu_dai、codec、platform 的
probe()
函数,完成物理设备的初始化;
- 随后调用
soc_new_pcm()
:- 设置 pcm native 中要使用的 pcm 操作函数,这些函数用于驱动音频物理设备,包括 machine、codec_dai、cpu_dai、platform;
- 调用
snd_pcm_new()
创建 pcm 逻辑设备,回放子流和录制子流都在这里创建; - 回调 platform 驱动的
pcm_new()
,完成音频 dma 设备初始化和 dma buffer 内存分配;
- 最后调用
snd_card_register()
注册声卡。
关于音频物理设备部分(Codec/Platform/Machine)不再累述,下面详细分析声卡和 PCM 逻辑设备的注册过程。
上面提到声卡驱动上挂着多个逻辑子设备,有 pcm 音频数据流、control 混音器、midi 迷笛、timer 定时器、sequencer 音序器等。
+-----------+
| snd_card |
+-----------+
| | |
+-----------+ | +------------+
| | |
+-----------+ +-----------+ +-----------+
| snd_pcm | |snd_control| | snd_timer | ...
+-----------+ +-----------+ +-----------+
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
这些与声音相关的逻辑设备都在结构体 snd_card
管理之下,可以说 snd_card
是 alsa 中最顶层的结构。我们再看看 alsa 声卡驱动的大致结构图(不是严格的 UML 类图,有结构体定义、模块关系、函数调用,方便标示结构模块的层次及关系):
snd_cards:记录着所注册的声卡实例,每个声卡实例有着各自的逻辑设备,如 PCM 设备、CTL 设备、MIDI 设备等,并一一记录到 snd_card
的 devices
链表上
snd_minors:记录着所有逻辑设备的上下文信息,它是声卡逻辑设备与系统调用 API 之间的桥梁;每个 snd_minor
在逻辑设备注册时被填充,在逻辑设备使用时就可以从该结构体中得到相应的信息(主要是系统调用函数集 file_operations
)
6.2. 声卡的创建
声卡实例通过函数 snd_card_create()
来创建,其函数原型:
/**
* snd_card_create - create and initialize a soundcard structure
* @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
* @xid: card identification (ASCII string)
* @module: top level module for locking
* @extra_size: allocate this extra size after the main soundcard structure
* @card_ret: the pointer to store the created card instance
*
* Creates and initializes a soundcard structure.
*
* The function allocates snd_card instance via kzalloc with the given
* space for the driver to use freely. The allocated struct is stored
* in the given card_ret pointer.
*
* Returns zero if successful or a negative error code.
*/
int snd_card_create(int idx, const char *xid,
struct module *module, int extra_size,
struct snd_card **card_ret)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
注释非常详细,简单说下:
- idx:声卡的编号,如为 -1,则由系统自动分配
- xid:声卡标识符,如为 NULL,则以
snd_card
的 shortname 或 longname 代替 - card_ret:返回所创建的声卡实例的指针
如下是我的计算机的声卡信息:
$ cat /proc/asound/cards
0 [PCH ]: HDA-Intel - HDA Intel PCH
HDA Intel PCH at 0xf7c30000 irq 47
- 1
- 2
- 3
- number:0
- id:PCH
- shortname:HDA Intel PCH
- longname:HDA Intel PCH at 0xf7c30000 irq 47
shortname、longname 常用于打印信息,上面的声卡信息是通过如下函数打印出来的:
static void snd_card_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { int idx, count; struct snd_card *card;
<span class="token keyword">for</span> <span class="token punctuation">(</span>idx <span class="token operator">=</span> count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> idx <span class="token operator"><</span> SNDRV_CARDS<span class="token punctuation">;</span> idx<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">mutex_lock</span><span class="token punctuation">(</span><span class="token operator">&</span>snd_card_mutex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>card <span class="token operator">=</span> snd_cards<span class="token punctuation">[</span>idx<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> count<span class="token operator">++</span><span class="token punctuation">;</span> <span class="token function">snd_iprintf</span><span class="token punctuation">(</span>buffer<span class="token punctuation">,</span> <span class="token string">"%2i [%-15s]: %s - %sn"</span><span class="token punctuation">,</span> idx<span class="token punctuation">,</span> card<span class="token operator">-></span>id<span class="token punctuation">,</span> card<span class="token operator">-></span>driver<span class="token punctuation">,</span> card<span class="token operator">-></span>shortname<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">snd_iprintf</span><span class="token punctuation">(</span>buffer<span class="token punctuation">,</span> <span class="token string">" %sn"</span><span class="token punctuation">,</span> card<span class="token operator">-></span>longname<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">mutex_unlock</span><span class="token punctuation">(</span><span class="token operator">&</span>snd_card_mutex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>count<span class="token punctuation">)</span> <span class="token function">snd_iprintf</span><span class="token punctuation">(</span>buffer<span class="token punctuation">,</span> <span class="token string">"--- no soundcards ---n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
6.3. 逻辑设备的创建
当声卡实例建立后,接着可以创建声卡下面的各个逻辑设备了。每个逻辑设备创建时,都会调用 snd_device_new()
生成一个 snd_device
实例,并把该实例挂到声卡 snd_card
的 devices
链表上。alsa 驱动为各种逻辑设备提供了创建接口,如下:
Device | Interface |
---|---|
PCM | snd_pcm_new() |
CONTROL | snd_ctl_create() |
MIDI | snd_rawmidi_new() |
TIMER | snd_timer_new() |
SEQUENCER | snd_seq_device_new() |
JACK | snd_jack_new() |
这些接口的一般过程如下:
int snd_xxx_new() { // 这些接口供逻辑设备注册时回调 static struct snd_device_ops ops = { .dev_free = snd_xxx_dev_free, .dev_register = snd_xxx_dev_register, .dev_disconnect = snd_xxx_dev_disconnect, };
<span class="token comment">// 逻辑设备实例初始化</span> <span class="token comment">// 新建一个设备实例 snd_device,挂到 snd_card 的 devices 链表上,把该逻辑设备纳入声卡的管理当中,SNDRV_DEV_xxx 是逻辑设备的类型</span> <span class="token keyword">return</span> <span class="token function">snd_device_new</span><span class="token punctuation">(</span>card<span class="token punctuation">,</span> SNDRV_DEV_xxx<span class="token punctuation">,</span> card<span class="token punctuation">,</span> <span class="token operator">&</span>ops<span class="token punctuation">)</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
其中 snd_device_ops
是声卡逻辑设备的注册函数集,dev_register()
回调尤其重要,它在声卡注册时被调用,用于建立系统的设备节点,/dev/snd/ 目录的设备节点都是在这里创建的,通过这些设备节点可系统调用 open/release/read/write/ioctl…
访问操作该逻辑设备。
例如 snd_ctl_dev_register()
:
// CTL 设备的系统调用接口
static const struct file_operations snd_ctl_f_ops =
{
.owner = THIS_MODULE,
.read = snd_ctl_read,
.open = snd_ctl_open,
.release = snd_ctl_release,
.llseek = no_llseek,
.poll = snd_ctl_poll,
.unlocked_ioctl = snd_ctl_ioctl,
.compat_ioctl = snd_ctl_ioctl_compat,
.fasync = snd_ctl_fasync,
};
/*
-
registration of the control device
/
static int snd_ctl_dev_register(struct snd_device device)
{
struct snd_card *card = device->device_data;
int err, cardnum;
char name[16];if (snd_BUG_ON(!card))
return -ENXIO;
cardnum = card->number;
if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
return -ENXIO;
sprintf(name, “controlC%i”, cardnum);
if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
&snd_ctl_f_ops, card, name)) < 0)
return err;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
事实是调用 snd_register_device_for_dev ()
:
- 分配并初始化一个
snd_minor
实例; - 保存该
snd_minor
实例到snd_minors
数组中; - 调用
device_create()
生成设备文件节点。
/** * snd_register_device_for_dev - Register the ALSA device file for the card * @type: the device type, SNDRV_DEVICE_TYPE_XXX * @card: the card instance * @dev: the device index * @f_ops: the file operations * @private_data: user pointer for f_ops->open() * @name: the device file name * @device: the &struct device to link this new device to * * Registers an ALSA device file for the given card. * The operators have to be set in reg parameter. * * Returns zero if successful, or a negative error code on failure. */ int snd_register_device_for_dev(int type, struct snd_card *card, int dev, const struct file_operations *f_ops, void *private_data, const char *name, struct device *device) { int minor; struct snd_minor *preg;
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">snd_BUG_ON</span><span class="token punctuation">(</span><span class="token operator">!</span>name<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator">-</span>EINVAL<span class="token punctuation">;</span> preg <span class="token operator">=</span> <span class="token function">kmalloc</span><span class="token punctuation">(</span><span class="token keyword">sizeof</span> <span class="token operator">*</span>preg<span class="token punctuation">,</span> GFP_KERNEL<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>preg <span class="token operator">==</span> <span class="token constant">NULL</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator">-</span>ENOMEM<span class="token punctuation">;</span> preg<span class="token operator">-></span>type <span class="token operator">=</span> type<span class="token punctuation">;</span> preg<span class="token operator">-></span>card <span class="token operator">=</span> card <span class="token operator">?</span> card<span class="token operator">-></span>number <span class="token punctuation">:</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span> preg<span class="token operator">-></span>device <span class="token operator">=</span> dev<span class="token punctuation">;</span> preg<span class="token operator">-></span>f_ops <span class="token operator">=</span> f_ops<span class="token punctuation">;</span> preg<span class="token operator">-></span>private_data <span class="token operator">=</span> private_data<span class="token punctuation">;</span> <span class="token function">mutex_lock</span><span class="token punctuation">(</span><span class="token operator">&</span>sound_mutex<span class="token punctuation">)</span><span class="token punctuation">;</span>
#ifdef CONFIG_SND_DYNAMIC_MINORS
minor = snd_find_free_minor(type);
#else
minor = snd_kernel_minor(type, card, dev);
if (minor >= 0 && snd_minors[minor])
minor = -EBUSY;
#endif
if (minor < 0) {
mutex_unlock(&sound_mutex);
kfree(preg);
return minor;
}
snd_minors[minor] = preg;
preg->dev = device_create(sound_class, device, MKDEV(major, minor),
private_data, “%s”, name);
if (IS_ERR(preg->dev)) {
snd_minors[minor] = NULL;
mutex_unlock(&sound_mutex);
minor = PTR_ERR(preg->dev);
kfree(preg);
return minor;
}
<span class="token function">mutex_unlock</span><span class="token punctuation">(</span><span class="token operator">&</span>sound_mutex<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
上面过程是声卡注册时才被回调的。
6.4. 声卡的注册
当声卡下的所有逻辑设备都已经准备就绪后,就可以调用 snd_card_register()
注册声卡了:
- 创建声卡的 sysfs 设备;
- 调用
snd_device_register_all()
注册所有挂在该声卡下的逻辑设备; - 建立 proc 信息文件和 sysfs 属性文件。
/** * snd_card_register - register the soundcard * @card: soundcard structure * * This function registers all the devices assigned to the soundcard. * Until calling this, the ALSA control interface is blocked from the * external accesses. Thus, you should call this function at the end * of the initialization of the card. * * Returns zero otherwise a negative error code if the registration failed. */ int snd_card_register(struct snd_card *card) { int err;
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">snd_BUG_ON</span><span class="token punctuation">(</span><span class="token operator">!</span>card<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator">-</span>EINVAL<span class="token punctuation">;</span> <span class="token comment">// 创建 sysfs 设备,声卡的 class 将会出现在 /sys/class/sound/ 下面</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>card<span class="token operator">-></span>card_dev<span class="token punctuation">)</span> <span class="token punctuation">{</span> card<span class="token operator">-></span>card_dev <span class="token operator">=</span> <span class="token function">device_create</span><span class="token punctuation">(</span>sound_class<span class="token punctuation">,</span> card<span class="token operator">-></span>dev<span class="token punctuation">,</span> <span class="token function">MKDEV</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> card<span class="token punctuation">,</span> <span class="token string">"card%i"</span><span class="token punctuation">,</span> card<span class="token operator">-></span>number<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">IS_ERR</span><span class="token punctuation">(</span>card<span class="token operator">-></span>card_dev<span class="token punctuation">)</span><span class="token punctuation">)</span> card<span class="token operator">-></span>card_dev <span class="token operator">=</span> <span class="token constant">NULL</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 遍历挂在该声卡的所有逻辑设备,回调各 snd_device 的 ops->dev_register() 完成各逻辑设备的注册</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>err <span class="token operator">=</span> <span class="token function">snd_device_register_all</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator"><</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">return</span> err<span class="token punctuation">;</span> <span class="token function">mutex_lock</span><span class="token punctuation">(</span><span class="token operator">&</span>snd_card_mutex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>snd_cards<span class="token punctuation">[</span>card<span class="token operator">-></span>number<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* already registered */</span> <span class="token function">mutex_unlock</span><span class="token punctuation">(</span><span class="token operator">&</span>snd_card_mutex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">*</span>card<span class="token operator">-></span>id<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* make a unique id name from the given string */</span> <span class="token keyword">char</span> tmpid<span class="token punctuation">[</span><span class="token keyword">sizeof</span><span class="token punctuation">(</span>card<span class="token operator">-></span>id<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token function">memcpy</span><span class="token punctuation">(</span>tmpid<span class="token punctuation">,</span> card<span class="token operator">-></span>id<span class="token punctuation">,</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>card<span class="token operator">-></span>id<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">snd_card_set_id_no_lock</span><span class="token punctuation">(</span>card<span class="token punctuation">,</span> tmpid<span class="token punctuation">,</span> tmpid<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">/* create an id from either shortname or longname */</span> <span class="token keyword">const</span> <span class="token keyword">char</span> <span class="token operator">*</span>src<span class="token punctuation">;</span> src <span class="token operator">=</span> <span class="token operator">*</span>card<span class="token operator">-></span>shortname <span class="token operator">?</span> card<span class="token operator">-></span>shortname <span class="token punctuation">:</span> card<span class="token operator">-></span>longname<span class="token punctuation">;</span> <span class="token function">snd_card_set_id_no_lock</span><span class="token punctuation">(</span>card<span class="token punctuation">,</span> src<span class="token punctuation">,</span> <span class="token function">retrieve_id_from_card_name</span><span class="token punctuation">(</span>src<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> snd_cards<span class="token punctuation">[</span>card<span class="token operator">-></span>number<span class="token punctuation">]</span> <span class="token operator">=</span> card<span class="token punctuation">;</span> <span class="token comment">// 把该声卡实例保存到 snd_cards 数组中</span> <span class="token function">mutex_unlock</span><span class="token punctuation">(</span><span class="token operator">&</span>snd_card_mutex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 声卡相关信息,见:/proc/asound/card0</span> <span class="token function">init_info_for_card</span><span class="token punctuation">(</span>card<span class="token punctuation">)</span><span class="token punctuation">;</span>
#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
if (snd_mixer_oss_notify_callback)
snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
#endif
// 声卡的 sysfs 属性节点
if (card->card_dev) {
err = device_create_file(card->card_dev, &card_id_attrs);
if (err < 0)
return err;
err = device_create_file(card->card_dev, &card_number_attrs);
if (err < 0)
return err;
}
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
至此完成了声卡及声卡下的所有逻辑设备的注册,用户态可以通过系统调用来访问这些设备了。
6.5. PCM 设备的创建
最后我们简单描述下 PCM 设备的建立过程:
snd_pcm_set_ops:设置 PCM 设备的操作接口,设置完成后,在 PCM 设备层即可访问操作底层音频物理设备。
snd_pcm_new:
- 创建一个 PCM 设备实例
snd_pcm
; - 创建 playback stream 和 capture stream,旗下的 substream 也同时建立;
- 调用
snd_device_new()
把 PCM 设备挂到声卡的devices
链表上。
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, .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 PCMn"); return -ENOMEM; } pcm->card = card; pcm->device = device; pcm->internal = internal; if (id) strlcpy(pcm->id, id, sizeof(pcm->id)); 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) { snd_pcm_free(pcm); return err; } if (rpcm) *rpcm = pcm; return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
我们再看看 PCM 设备的系统调用:
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,
}
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
snd_pcm_f_ops
作为 snd_register_device_for_dev()
的参数被传入,并被记录在 snd_minors[minor]
中的字段 f_ops
中。snd_pcm_f_ops[0]
是回放使用的系统调用接口,snd_pcm_f_ops[1]
是录制使用的系统调用接口。
7. Frame & Period
后面章节将分析 dma buffer 的管理,其中细节需要对音频数据相关概念有一定的了解。因此本章说明下音频数据中的几个重要概念:
- Sample:样本长度,音频数据最基本的单位,常见的有 8 位和 16 位;
- Channel:声道数,分为单声道 mono 和立体声 stereo;
- Frame:帧,构成一个完整的声音单元,所谓的声音单元是指一个采样样本,
Frame = Sample * channel
; - Rate:又称 sample rate,采样率,即每秒的采样次数,针对帧而言;
- Period Size:周期,每次硬件中断处理音频数据的帧数,对于音频设备的数据读写,以此为单位;
- Buffer Size:数据缓冲区大小,这里指 runtime 的 buffer size,而不是结构图
snd_pcm_hardware
中定义的 buffer_bytes_max;一般来说buffer_size = period_size * period_count
, period_count 相当于处理完一个 buffer 数据所需的硬件中断次数。
下面一张图直观的表示 buffer/period/frame/sample 之间的关系:
read/write pointer | v +-----------------------------+--------------+--------------+ | | | | |buffer = 4 periods +--------------+--------------+--------------+--------------+ ^ | +---+---+------+ | | | ... |period = 1024 frames +---+---+------+ ^ | +---+ |L|R|frame = 2 samples (left + right) +---+
sample = 2 bytes (16bit)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
这个 buffer 中有 4 个 period,每当 DMA 搬运完一个 period 的数据就会出生一次中断,因此搬运这个 buffer 中的数据将产生 4 次中断。ALSA 为什么这样做?因为数据缓存区可能很大,一次传输可能会导致不可接受的延迟;为了解决这个问题,alsa 把缓存区拆分成多个周期,以周期为单元传输数据。
7.1. Frames & Periods
敏感的读者会察觉到 period 和 buffer size 在 PCM 数据搬运中扮演着非常重要的角色。下面引用两段来自 alsa 官网对 Period 的详细解释:
Period
The interval between interrupts from the hardware. This defines the input latency, since the CPU will not have any idea that there is data waiting until the audio interface interrupts it.
The audio interface has a “pointer” that marks the current position for read/write in its h/w buffer. The pointer circles around the buffer as long as the interface is running.
Typically, there are an integral number of periods per traversal of the h/w buffer, but not always. There is at least one card (ymfpci) that generates interrupts at a fixed rate indepedent of the buffer size (which can be changed), resulting in some “odd” effects compared to more traditional designs.
Note: h/w generally defines the interrupt in frames, though not always.
Alsa’s period size setting will affect how much work the CPU does. if you set the period size low, there will be more interrupts and the work that is done every interrupt will be done more often. So, if you don’t care about low latency, set the period size large as possible and you’ll have more CPU cycles for other things. The defaults that ALSA provides are in the middle of the range, typically.
(from an old AlsaDevel thread[1], quoting Paul Davis)
Retrieved from “http://alsa.opensrc.org/Period”
FramesPeriods
A frame is equivalent of one sample being played, irrespective of the number of channels or the number of bits. e.g.
- 1 frame of a Stereo 48khz 16bit PCM stream is 4 bytes.
- 1 frame of a 5.1 48khz 16bit PCM stream is 12 bytes.
A period is the number of frames in between each hardware interrupt. The poll() will return once a period.
The buffer is a ring buffer. The buffer size always has to be greater than one period size. Commonly this is 2*period size, but some hardware can do 8 periods per buffer. It is also possible for the buffer size to not be an integer multiple of the period size.
Now, if the hardware has been set to 48000Hz , 2 periods, of 1024 frames each, making a buffer size of 2048 frames. The hardware will interrupt 2 times per buffer. ALSA will endeavor to keep the buffer as full as possible. Once the first period of samples has been played, the third period of samples is transfered into the space the first one occupied while the second period of samples is being played. (normal ring buffer behaviour).
Additional example
Here is an alternative example for the above discussion.
Say we want to work with a stereo, 16-bit, 44.1 KHz stream, one-way (meaning, either in playback or in capture direction). Then we have:
- ‘stereo’ = number of channels: 2
- 1 analog sample is represented with 16 bits = 2 bytes
- 1 frame represents 1 analog sample from all channels; here we have 2 channels, and so: 1 frame = (num_channels) * (1 sample in bytes) = (2 channels) * (2 bytes (16 bits) per sample) = 4 bytes (32 bits)
- To sustain 2x 44.1 KHz analog rate - the system must be capable of data transfer rate, in Bytes/sec: Bps_rate = (num_channels) * (1 sample in bytes) * (analog_rate) = (1 frame) * (analog_rate) = ( 2 channels ) * (2 bytes/sample) * (44100 samples/sec) = 2244100 = 176400 Bytes/sec
Now, if ALSA would interrupt each second, asking for bytes - we’d need to have 176400 bytes ready for it (at end of each second), in order to sustain analog 16-bit stereo @ 44.1Khz.
- If it would interrupt each half a second, correspondingly for the same stream we’d need 176400/2 = 88200 bytes ready, at each interrupt;
- if the interrupt hits each 100 ms, we’d need to have 176400*(0.1/1) = 17640 bytes ready, at each interrupt.
We can control when this PCM interrupt is generated, by setting a period size, which is set in frames.
- Thus, if we set 16-bit stereo @ 44.1Khz, and the period_size to 4410 frames => (for 16-bit stereo @ 44.1Khz, 1 frame equals 4 bytes - so 4410 frames equal 4410*4 = 17640 bytes) => an interrupt will be generated each 17640 bytes - that is, each 100 ms.
- Correspondingly, buffer_size should be at least 2period_size = 24410 = 8820 frames (or 8820*4 = 35280 bytes).
It seems (writing-an-alsa-driver.pdf), however, that it is the ALSA runtime that decides on the actual buffer_size and period_size, depending on: the requested number of channels, and their respective properties (rate and sampling resolution) - as well as the parameters set in the snd_pcm_hardware structure (in the driver).
Also, the following quote may be relevant, from “(alsa-devel) Questions about writing a new ALSA driver for a very limitted device”:
The “frame” represents the unit, 1 frame = # channels x sample_bytes.
In your case, 1 frame corresponds to 2 channels x 16 bits = 4 bytes.The periods is the number of periods in a ring-buffer. In OSS, called
as “fragments”.So,
- buffer_size = period_size * periods
- period_bytes = period_size * bytes_per_frame
- bytes_per_frame = channels * bytes_per_sample
I still don’t understand what ‘period_size’ and a ‘period’ is?
The “period” defines the frequency to update the status, usually via the invokation of interrupts. The “period_size” defines the frame sizes corresponding to the “period time”. This term corresponds to the “fragment size” on OSS. On major sound hardwares, a ring-buffer is divided to several parts and an irq is issued on each boundary. The period_size defines the size of this chunk.
On some hardwares, the irq is controlled on the basis of a timer. In this case, the period is defined as the timer frequency to invoke an irq.
这里不做翻译了,简单说下 Frame 和 Period 要点:
- Frame:帧,构成一个完整的声音单元,它的大小等于 sample_bits * channels;
- Peroid:周期大小,即每次 dma 运输处理音频数据的帧数。如果周期大小设定得较大,则单次处理的数据较多,这意味着单位时间内硬件中断的次数较少,CPU 也就有更多时间处理其他任务,功耗也更低,但这样也带来一个显著的弊端——数据处理的时延会增大。
再说说 period bytes,对于 dma 处理来说,它直接关心的是数据大小,而非 period_size(一个周期的帧数),有个转换关系:period_bytes = period_size * sample_bits * channels / 8
由于 I2S 总线采样率是稳定的,我们可以计算 I2S 传输一个周期的数据所需的时间:transfer_time = 1 * period_size / sample_rate, in second
例如 period_size = 1024,sample_rate = 48KHz ,那么一个周期数据的传输时间是: 1 * 1024 / 48000 = 21.3 (ms)。
7.2. hrtimer 模拟 PCM 周期中断
在 4.2.1. pcm operations
章节中,我们提到:每次 dma 传输完成一个周期的数据传输后,都要调用 snd_pcm_period_elapsed()
告知 pcm native 一个周期的数据已经传送到 FIFO 上了,然后再次调用 dma 传输音频数据…如此循环。
但有些 Platform 可能由于设计如此或设计缺陷,dma 传输完一个周期的数据不会产生硬件中断。这样系统如何知道什么时候传输完一个周期的数据了呢?在上个章节的最后,我们提到 I2S 总线传输一个周期的数据所需的时间,这其实也是 dma 搬运一个周期的数据所需的时间,这很容易理解:I2S FIFO 消耗完一个周期的数据,dma 才接着搬运一个周期的数据到 I2S FIFO。
因此我们可以用定时器来模拟这种硬件中断:
- 触发dma搬运数据时,启动定时器开始计时;
- 当定时到
1 * period_size / sample_rate
,这时 I2S 已传输完一个周期的音频数据了,进入定时器中断处理:调用snd_pcm_period_elapsed()
告知 pcm native 一个周期的数据已经处理完毕了,同时准备下一次的数据搬运; - 继续执行步骤 1…
为了更好保证数据传输的实时性,建议采用高精度定时器 hrtimer。
作者见过至少两家芯片在传输音频数据时需要用定时器模拟周期中断,一是 MTK 的智能手机处理器,二是 Freescale 的 i.MX 系列处理器。后者已经合入 Linux 内核代码,具体见:sound/soc/imx/imx-pcm-fiq.c
,这里简略分析:
// 定时器中断处理例程 static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) { ...
<span class="token comment">/* If we've transferred at least a period then report it and * reset our poll time */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>delta <span class="token operator">>=</span> iprtd<span class="token operator">-></span>period<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">snd_pcm_period_elapsed</span><span class="token punctuation">(</span>substream<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 告知 pcm native 一个周期的数据已经处理完毕</span> iprtd<span class="token operator">-></span>last_offset <span class="token operator">=</span> iprtd<span class="token operator">-></span>offset<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">hrtimer_forward_now</span><span class="token punctuation">(</span>hrt<span class="token punctuation">,</span> <span class="token function">ns_to_ktime</span><span class="token punctuation">(</span>iprtd<span class="token operator">-></span>poll_time_ns<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 重新计时,poll_time_ns:I2S 传输一个周期的数据所需的时间</span> <span class="token keyword">return</span> HRTIMER_RESTART<span class="token punctuation">;</span>
}
// hw_params 回调,数据传输开始前,先设置 dma 传输参数
static int snd_imx_pcm_hw_params(struct snd_pcm_substream substream,
struct snd_pcm_hw_params params)
{
struct snd_pcm_runtime runtime = substream->runtime;
struct imx_pcm_runtime_data iprtd = runtime->private_data;
iprtd<span class="token operator">-></span>size <span class="token operator">=</span> <span class="token function">params_buffer_bytes</span><span class="token punctuation">(</span>params<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// dma 缓冲区大小</span>
iprtd<span class="token operator">-></span>periods <span class="token operator">=</span> <span class="token function">params_periods</span><span class="token punctuation">(</span>params<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 周期数</span>
iprtd<span class="token operator">-></span>period <span class="token operator">=</span> <span class="token function">params_period_bytes</span><span class="token punctuation">(</span>params<span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// 周期大小</span>
iprtd<span class="token operator">-></span>offset <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
iprtd<span class="token operator">-></span>last_offset <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
iprtd<span class="token operator">-></span>poll_time_ns <span class="token operator">=</span> <span class="token number">1000000000</span> <span class="token operator">/</span> <span class="token function">params_rate</span><span class="token punctuation">(</span>params<span class="token punctuation">)</span> <span class="token operator">*</span>
<span class="token function">params_period_size</span><span class="token punctuation">(</span>params<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 计算 I2S 传输一个周期的数据所需的时间</span>
<span class="token function">snd_pcm_set_runtime_buffer</span><span class="token punctuation">(</span>substream<span class="token punctuation">,</span> <span class="token operator">&</span>substream<span class="token operator">-></span>dma_buffer<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 设置 dma 缓冲区</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
}
// trigger 回调,触发 dma 传输或停止
static int snd_imx_pcm_trigger(struct snd_pcm_substream substream, int cmd)
{
struct snd_pcm_runtime runtime = substream->runtime;
struct imx_pcm_runtime_data *iprtd = runtime->private_data;
<span class="token keyword">switch</span> <span class="token punctuation">(</span>cmd<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">case</span> SNDRV_PCM_TRIGGER_START<span class="token punctuation">:</span>
<span class="token keyword">case</span> SNDRV_PCM_TRIGGER_RESUME<span class="token punctuation">:</span>
<span class="token keyword">case</span> SNDRV_PCM_TRIGGER_PAUSE_RELEASE<span class="token punctuation">:</span>
<span class="token function">atomic_set</span><span class="token punctuation">(</span><span class="token operator">&</span>iprtd<span class="token operator">-></span>running<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">hrtimer_start</span><span class="token punctuation">(</span><span class="token operator">&</span>iprtd<span class="token operator">-></span>hrt<span class="token punctuation">,</span> <span class="token function">ns_to_ktime</span><span class="token punctuation">(</span>iprtd<span class="token operator">-></span>poll_time_ns<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// 准备传输数据,启动定时器,开始计时</span>
HRTIMER_MODE_REL<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">++</span>fiq_enable <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">)</span>
<span class="token function">enable_fiq</span><span class="token punctuation">(</span>imx_pcm_fiq<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 开始 dma 传输</span>
<span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token keyword">case</span> SNDRV_PCM_TRIGGER_STOP<span class="token punctuation">:</span>
<span class="token keyword">case</span> SNDRV_PCM_TRIGGER_SUSPEND<span class="token punctuation">:</span>
<span class="token keyword">case</span> SNDRV_PCM_TRIGGER_PAUSE_PUSH<span class="token punctuation">:</span>
<span class="token function">atomic_set</span><span class="token punctuation">(</span><span class="token operator">&</span>iprtd<span class="token operator">-></span>running<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">--</span>fiq_enable <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span>
<span class="token function">disable_fiq</span><span class="token punctuation">(</span>imx_pcm_fiq<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 停止 dma 传输</span>
<span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token keyword">default</span><span class="token punctuation">:</span>
<span class="token keyword">return</span> <span class="token operator">-</span>EINVAL<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
– to be continued…
</div>
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-b6c3c6d139.css" rel="stylesheet">
<div class="more-toolbox">
<div class="left-toolbox">
<ul class="toolbox-list">
<li class="tool-item tool-active is-like "><a href="javascript:;"><svg class="icon" aria-hidden="true">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#csdnc-thumbsup"></use>
</svg><span class="name">点赞</span>
<span class="count">11</span>
</a></li>
<li class="tool-item tool-active is-collection "><a href="javascript:;" data-report-click="{"mod":"popu_824"}"><svg class="icon" aria-hidden="true">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-csdnc-Collection-G"></use>
</svg><span class="name">收藏</span></a></li>
<li class="tool-item tool-active is-share"><a href="javascript:;"><svg class="icon" aria-hidden="true">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-csdnc-fenxiang"></use>
</svg>分享</a></li>
<!--打赏开始-->
<!--打赏结束-->
<li class="tool-item tool-more">
<a>
<svg t="1575545411852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M179.176 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5718"></path><path d="M509.684 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5719"></path><path d="M846.175 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5720"></path></svg>
</a>
<ul class="more-box">
<li class="item"><a class="article-report">文章举报</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div class="person-messagebox">
<div class="left-message"><a href="https://blog.csdn.net/zyuanyun">
<img src="https://profile.csdnimg.cn/5/D/E/3_zyuanyun" class="avatar_pic" username="zyuanyun">
<img src="https://g.csdnimg.cn/static/user-reg-year/1x/3.png" class="user-years">
</a></div>
<div class="middle-message">
<div class="title"><span class="tit"><a href="https://blog.csdn.net/zyuanyun" data-report-click="{"mod":"popu_379"}" target="_blank">zyuanyun</a></span>
</div>
<div class="text"><span>发布了4 篇原创文章</span> · <span>获赞 93</span> · <span>访问量 8万+</span></div>
</div>
<div class="right-message">
<a href="https://im.csdn.net/im/main.html?userName=zyuanyun" target="_blank" class="btn btn-sm btn-red-hollow bt-button personal-letter">私信
</a>
<a class="btn btn-sm attented bt-button personal-watch" data-report-click="{"mod":"popu_379"}">已关注</a>
</div>
</div>
</div>
</article>
<script>
$("#blog_detail_zk_collection").click(function(){
window.csdn.articleCollection()
})
<div class="recommend-box"><div class="recommend-item-box type_blog clearfix" data-report-click="{"mod":"popu_614","dest":"https://blog.csdn.net/yankunkunkun/article/details/17535851","strategy":"BlogCommendFromMachineLearnPai2","index":"0"}">
<div class="content" style="width: 852px;">
<a href="https://blog.csdn.net/yankunkunkun/article/details/17535851" target="_blank" rel="noopener" title="ALSA驱动分析,比ALSA官方文档好理解多了">
<h4 class="text-truncate oneline" style="width: 692px;">
<em>ALSA</em>驱动分析,比<em>ALSA</em>官方文档好理解多了 </h4>
<div class="info-box d-flex align-content-center">
<p class="date-and-readNum oneline">
<span class="date hover-show">12-24</span>
<span class="read-num hover-hide">
阅读数
987</span>
</p>
</div>
</a>
<p class="content" style="width: 852px;">
<a href="https://blog.csdn.net/yankunkunkun/article/details/17535851" target="_blank" rel="noopener" title="ALSA驱动分析,比ALSA官方文档好理解多了">
<span class="desc oneline">Linux ALSA声卡驱动之一:ALSA架构简介 http://blog.csdn.net/droidphone/article/details/6271122Linux ALSA声卡驱动之二:声卡...</span>
</a>
<span class="blog_title_box oneline ">
<span class="type-show type-show-blog type-show-after">博文</span>
<a target="_blank" rel="noopener" href="https://blog.csdn.net/yankunkunkun">来自: <span class="blog_title"> yankunkunkun的专栏</span></a>
</span>
</p>
</div>
</div>
大神,收下我的膝盖举报回复
-
Qidi_Huang 2年前 学习了。感谢分享。 举报回复
- 上一页
- 1
- 下一页
</div>
linux驱动由浅入深系列:ALSA框架详解 音频子系统之二
03-04 阅读数 6321
linux驱动由浅入深系列:tinyalsa(tinymix/tinycap/tinyplay/tinypcminfo)音频子系统之一linux驱动由浅入深系列:ALSA框架详解 音频子系统之二本文以... 博文 来自: Radia的专栏
Linux ALSA音频系统:platform,machine,codec
07-15 阅读数 1967
1.前言 本篇结合自己的项目,参考CSDN博主:zyuanyun 来讲解。2.项目平台介绍Kernel - 4.9Soc - Amlogic (型号保密)CODEC - npcp215xMachin... 博文 来自: ccion的博客
Linux ALSA声卡驱动之七:ASoC架构中的Codec
02-23 阅读数 5万+
1. Codec简介在移动设备中,Codec的作用可以归结为4种,分别是:对PCM等信号进行D/A转换,把数字的音频信号转换为模拟信号对Mic、Linein或者其他输入源的模拟信号进行A/D转换,把... 博文 来自: DroidPhone的专栏
<div class="recommend-item-box recommend-recommend-box"><div id="kp_box_59" data-pid="59"><script type="text/javascript">
(function() {
var s = "_" + Math.random().toString(36).slice(2);
document.write('<div style="" id="' + s + '"></div>');
(window.slotbydup = window.slotbydup || []).push({
id: "u3491668",
container: s
});
})();
Linux ALSA音频系统:soundcard
09-01 阅读数 708
8.1声卡和PCM设备的建立过程 前面分析了codec,platform,machine驱动的组成部分及其注册过程,这三者都是物理设备相关的。 pcm逻辑设备,我们习惯称之为PCM中间层或pc... 博文 来自: ccion的博客
程序员必须掌握的核心算法有哪些?
12-26 阅读数 22万+
由于我之前一直强调数据结构以及算法学习的重要性,所以就有一些读者经常问我,数据结构与算法应该要学习到哪个程度呢?,说实话,这个问题我不知道要怎么回答你,主要取决于你想学习到哪些程度,不过针对这个问题,... 博文 来自: 帅地
Java学习的正确打开方式
01-08 阅读数 13万+
在博主认为,对于入门级学习java的最佳学习方法莫过于视频+博客+书籍+总结,前三者博主将淋漓尽致地挥毫于这篇博客文章中,至于总结在于个人,实际上越到后面你会发现学习的最好方式就是阅读参考官方文档其次... 博文 来自: 程序员宜春的博客
Linux ALSA 音频系统:物理链路篇
03-01 阅读数 1万+
1.概述硬件平台及软件版本:Kernel-3.4.5SoC-SamsungexynosCODEC-WM8994Machine-goni_wm8994Userspace-tinyalsaLinuxALS... 博文 来自: 络纭
程序员接私活怎样防止做完了不给钱?
10-31 阅读数 13万+
首先跟大家说明一点,我们做 IT 类的外包开发,是非标品开发,所以很有可能在开发过程中会有这样那样的需求修改,而这种需求修改很容易造成扯皮,进而影响到费用支付,甚至出现做完了项目收不到钱的情况。那么,... 博文 来自: DavidGoGo_的博客
<div class="recommend-item-box recommend-recommend-box"><div id="kp_box_60" data-pid="60"><iframe src="https://adaccount.csdn.net/#/preview/1119?m=JctyncHpDLpJnLUJJinmLAHcbAStvLXpnnbSALboHXJQHAcSccpotSpHntQWibUQEUEpiiSnpbJALAJJJnQmEbUAncAcHJcnQtnQ&k=" frameborder="0" width="100%" height="75px" scrolling="no"></iframe><img class="pre-img-lasy" data-src="https://kunyu.csdn.net/1.png?d=2&k=&m=JctyncHpDLpJnLUJJinmLAHcbAStvLXpnnbSALboHXJQHAcSccpotSpHntQWibUQEUEpiiSnpbJALAJJJnQmEbUAncAcHJcnQtnQ"></div></div>
网页实现一个简单的音乐播放器(大佬别看。(⊙﹏⊙))
10-30 阅读数 5万+
今天闲着无事,就想写点东西。然后听了下歌,就打算写个播放器。于是乎用h5 audio的加上js简单的播放器完工了。演示地点演示html代码如下` music 这个年纪 七月的风... 博文 来自: qq_44210563的博客
Linux文件操作高频使用命令
08-28 阅读数 8万+
文章目录0.新建操作:1.查看操作2.删除操作3.复制操作4.移动操作:5.重命名操作:6.解压压缩操作0.新建操作:mkdir abc #新建一个文件夹touch abc.sh #新建一个文件1.查... 博文 来自: 不能如期而至的专栏
<div class="recommend-item-box blog-expert-recommend-box" style="display: block;">
<div class="d-flex">
<div class="blog-expert-recommend">
<div class="blog-expert">
<div class="blog-expert-flexbox" data-report-view="{"mod":"popu_709","dest":"https://blog.csdn.net/zyuanyun/article/details/59180272"}"><div class="blog-expert-item"><div class="blog-expert-info-box"><div class="blog-expert-img-box" data-report-click="{"mod":"popu_709","dest":"https://blog.csdn.net/zyuanyun/article/details/59180272"}"><a href="https://blog.csdn.net/yankunkunkun" target="_blank"><img src="https://profile.csdnimg.cn/F/E/2/3_yankunkunkun" username="yankunkunkun" alt="maidong600" title="maidong600"></a><span data-report-click="{"mod":"popu_710","dest":"https://blog.csdn.net/zyuanyun/article/details/59180272"}"><span class="blog-expert-button-follow btn-red-follow" data-name="yankunkunkun" data-nick="maidong600">关注</span></span></div><div class="info"><span data-report-click="{"mod":"popu_709","dest":"https://blog.csdn.net/zyuanyun/article/details/59180272"}"><a href="https://blog.csdn.net/yankunkunkun" target="_blank"><h5 class="oneline" title="maidong600">maidong600</h5></a></span> <p></p><p class="article-num" title="5篇文章"> 5篇文章</p><p class="article-num" title="排名:千里之外"> 排名:千里之外</p><p></p></div></div></div><div class="blog-expert-item"><div class="blog-expert-info-box"><div class="blog-expert-img-box" data-report-click="{"mod":"popu_709","dest":"https://blog.csdn.net/zyuanyun/article/details/59180272"}"><a href="https://blog.csdn.net/RadianceBlau" target="_blank"><img src="https://profile.csdnimg.cn/E/C/C/3_radianceblau" username="RadianceBlau" alt="RadianceBlau" title="RadianceBlau"></a><span data-report-click="{"mod":"popu_710","dest":"https://blog.csdn.net/zyuanyun/article/details/59180272"}"><span class="blog-expert-button-follow btn-red-follow" data-name="RadianceBlau" data-nick="RadianceBlau">关注</span></span></div><div class="info"><span data-report-click="{"mod":"popu_709","dest":"https://blog.csdn.net/zyuanyun/article/details/59180272"}"><a href="https://blog.csdn.net/RadianceBlau" target="_blank"><h5 class="oneline" title="RadianceBlau">RadianceBlau</h5></a></span> <p></p><p class="article-num" title="88篇文章"> 88篇文章</p><p class="article-num" title="排名:千里之外"> 排名:千里之外</p><p></p></div></div></div><div class="blog-expert-item"><div class="blog-expert-info-box"><div class="blog-expert-img-box" data-report-click="{"mod":"popu_709","dest":"https://blog.csdn.net/zyuanyun/article/details/59180272"}"><a href="https://blog.csdn.net/weixin_41965270" target="_blank"><img src="https://profile.csdnimg.cn/7/E/6/3_weixin_41965270" username="weixin_41965270" alt="CNccion" title="CNccion"></a><span data-report-click="{"mod":"popu_710","dest":"https://blog.csdn.net/zyuanyun/article/details/59180272"}"><span class="blog-expert-button-follow btn-red-follow" data-name="weixin_41965270" data-nick="CNccion">关注</span></span></div><div class="info"><span data-report-click="{"mod":"popu_709","dest":"https://blog.csdn.net/zyuanyun/article/details/59180272"}"><a href="https://blog.csdn.net/weixin_41965270" target="_blank"><h5 class="oneline" title="CNccion">CNccion</h5></a></span> <p></p><p class="article-num" title="29篇文章"> 29篇文章</p><p class="article-num" title="排名:千里之外"> 排名:千里之外</p><p></p></div></div></div><div class="blog-expert-item"><div class="blog-expert-info-box"><div class="blog-expert-img-box" data-report-click="{"mod":"popu_709","dest":"https://blog.csdn.net/zyuanyun/article/details/59180272"}"><a href="https://blog.csdn.net/DroidPhone" target="_blank"><img src="https://profile.csdnimg.cn/8/B/5/3_droidphone" username="DroidPhone" alt="DroidPhone" title="DroidPhone"></a><span data-report-click="{"mod":"popu_710","dest":"https://blog.csdn.net/zyuanyun/article/details/59180272"}"><span class="blog-expert-button-follow btn-red-follow" data-name="DroidPhone" data-nick="DroidPhone">关注</span></span></div><div class="info"><span data-report-click="{"mod":"popu_709","dest":"https://blog.csdn.net/zyuanyun/article/details/59180272"}"><a href="https://blog.csdn.net/DroidPhone" target="_blank"><h5 class="oneline" title="DroidPhone">DroidPhone</h5></a></span> <p></p><p class="article-num" title="55篇文章"> 55篇文章</p><p class="article-num" title="排名:2000+"> 排名:2000+</p><p></p></div></div></div></div>
</div>
</div>
</div>
</div>
花了20分钟,给女朋友们写了一个web版群聊程序
11-28 阅读数 23万+
参考博客[1]https://www.byteslounge.com/tutorials/java-ee-html5-websocket-example 博文
我的 Input框 不可能这么可爱
09-03 阅读数 9万+
作者:陈大鱼头github: KRISACHAN<input /> 标签是我们日常开发中非常常见的替换元素了,但是最近在刷 whattwg 跟 MDN 的时候发现 跟 <input ... 博文 来自: 鱼头的Web海洋
从入门到精通,Java学习路线导航(附学习资源)
09-16 阅读数 7万+
引言最近也有很多人来向我"请教",他们大都是一些刚入门的新手,还不了解这个行业,也不知道从何学起,开始的时候非常迷茫,实在是每天回复很多人也很麻烦,所以在这里统一作个回复吧。Java学习路线当然,这里... 博文 来自: java_sha的博客
<div class="recommend-item-box recommend-recommend-box"><div id="kp_box_61" data-pid="61"><iframe src="https://adaccount.csdn.net/#/preview/787?m=iJSLcfEvHDAcSSSopHcEEEUbXvbSnEbHXcpSLAHyAnEmnAyHQEttApJLiWSEEEnLEcELmLSnXpULmEDobEEEUHipiEttcAQiEAbQ&k=" frameborder="0" width="100%" height="75px" scrolling="no"></iframe><img class="pre-img-lasy" data-src="https://kunyu.csdn.net/1.png?d=2&k=&m=iJSLcfEvHDAcSSSopHcEEEUbXvbSnEbHXcpSLAHyAnEmnAyHQEttApJLiWSEEEnLEcELmLSnXpULmEDobEEEUHipiEttcAQiEAbQ"></div></div>
相见恨晚的超实用网站
01-06 阅读数 8万+
相见恨晚的超实用网站持续更新中。。。 博文 来自: 藏冰的博客
字节跳动视频编解码面经
11-20 阅读数 6万+
三四月份投了字节跳动的实习(图形图像岗位),然后hr打电话过来问了一下会不会opengl,c++,shador,当时只会一点c++,其他两个都不会,也就直接被拒了。七月初内推了字节跳动的提前批,因为内... 博文 来自: ljh_shuai的博客
Linux ALSA音频系统之音频播放
06-14 阅读数 3135
1.amixer设置a.查询哪些参数可以控制 #amixer controls numid=2,iface=MIXER,name='DIN source' #... 博文 来自: al86866365的专栏
linux alsa 音频路径切换
05-14 阅读数 1万+
linux alsa 音频路径切换 博文 来自: xiaojsj111的专栏
Linux音频子系统(三)ALSA的核心层声卡注册
08-05 阅读数 51
前一章节对整个ALSA的框架进行了分层,本章主要来梳理下ALSA的驱动框架层。1. 声卡驱动初始化static int __init alsa_sound_init(void){ snd_major ... 博文 来自: 奇小葩
<div class="recommend-item-box recommend-recommend-box"><div id="kp_box_62" data-pid="62"><iframe src="https://adaccount.csdn.net/#/preview/260?m=cQpSbQHitALEiLcJnDtyEJHpncEpiQLmJSppAcJpSHpXtSASbAipnDHDtXQnSHtiQnWcvSLbEtUSAnvJLbnJmiUcfHpcQSEEtnnQ&k=" frameborder="0" width="100%" height="75px" scrolling="no"></iframe><img class="pre-img-lasy" data-src="https://kunyu.csdn.net/1.png?d=2&k=&m=cQpSbQHitALEiLcJnDtyEJHpncEpiQLmJSppAcJpSHpXtSASbAipnDHDtXQnSHtiQnWcvSLbEtUSAnvJLbnJmiUcfHpcQSEEtnnQ"></div></div>
linux系列之常用运维命令整理笔录
11-02 阅读数 18万+
本博客记录工作中需要的linux运维命令,大学时候开始接触linux,会一些基本操作,可是都没有整理起来,加上是做开发,不做运维,有些命令忘记了,所以现在整理成博客,当然vi,文件操作等就不介绍了,慢... 博文 来自: Nicky's blog
数据库优化 - SQL优化
11-01 阅读数 14万+
以实际SQL入手,带你一步一步走上SQL优化之路! 博文 来自: 飘渺Jam的博客
linux 查看声卡设备并测试录音 (ALSA 音频工具)
08-07 阅读数 400
测试环境 ubuntu12 与其他linux 系统大同小异 高级Linux声音体系(英语:Advanced Linux Sound Architecture,缩写为ALSA)是Linux内核中,... 博文 来自: fly_sky_share的博客
Linux ALSA声卡驱动之五:移动设备中的ALSA(ASoC)
01-17 阅读数 4万+
1. ASoC的由来ASoC--ALSA System on Chip ,是建立在标准ALSA驱动层上,为了更好地支持嵌入式处理器和移动设备中的音频Codec的一套软件体系。在ASoc出现之前,内核... 博文 来自: DroidPhone的专栏
通俗易懂地给女朋友讲:线程池的内部原理
11-04 阅读数 8万+
餐盘在灯光的照耀下格外晶莹洁白,女朋友拿起红酒杯轻轻地抿了一小口,对我说:“经常听你说线程池,到底线程池到底是个什么原理?”... 博文 来自: 万猫学社
<div class="recommend-item-box recommend-recommend-box"><div id="kp_box_63" data-pid="63"><script async="async" charset="utf-8" src="https://shared.ydstatic.com/js/yatdk/3.0.1/stream.js" data-id="8935aa488dd58452b9e5ee3b44f1212f" data-div-style="width:100%;" data-tit-style="margin-bottom: 6px; font-size: 18px; line-height: 24px; color: #3d3d3d;display: inline-block;font-weight:bold;" data-des-style="font-size: 13px; line-height: 22px; white-space: normal; color: #999;" data-img-style="float:left;margin-right:15px;width:90px;height:60px;" data-is-handling="1">
比特币原理详解
10-29 阅读数 15万+
一、什么是比特币比特币是一种电子货币,是一种基于密码学的货币,在2008年11月1日由中本聪发表比特币白皮书,文中提出了一种去中心化的电子记账系统,我们平时的电子现金是银行来记账,因为银行的背后是国家... 博文 来自: zcg_741454897的博客
经典算法(5)杨辉三角
11-04 阅读数 6万+
杨辉三角 是经典算法,这篇博客对它的算法思想进行了讲解,并有完整的代码实现。... 博文 来自: 扬帆向海的博客
Spring Boot 2.0(四):使用 Docker 部署 Spring Boot
03-19 阅读数 504
Docker 技术发展为微服务落地提供了更加便利的环境,使用 Docker 部署 Spring Boot 其实非常简单,这篇文章我们就来简单学习下。首先构建一个简单的 Spring Boot 项目,然... 博文 来自: weixin_34192732的博客
深入了解ALSA
12-14 阅读数 2万+
原文 http://www.volkerschatz.com/noise/alsa.htmlIntro任何人如果经常的使用linux机器处理音乐,那么他迟早会和ALSA打交道。ALSA是Advance... 博文 来自: kickxxx的专栏
英特尔不为人知的 B 面
11-05 阅读数 2万+
从 PC 时代至今,众人只知在 CPU、GPU、XPU、制程、工艺等战场中,英特尔在与同行硬件芯片制造商们的竞争中杀出重围,且在不断的成长进化中,成为全球知名的半导体公司。殊不知,在「刚硬」的背后,英... 博文 来自: CSDN资讯
<div class="recommend-item-box recommend-recommend-box"><div id="kp_box_64" data-pid="64"><script async="async" charset="utf-8" src="https://shared.ydstatic.com/js/yatdk/3.0.1/stream.js" data-id="8935aa488dd58452b9e5ee3b44f1212f" data-div-style="width:100%;" data-tit-style="margin-bottom: 6px; font-size: 18px; line-height: 24px; color: #3d3d3d;display: inline-block;font-weight:bold;" data-des-style="font-size: 13px; line-height: 22px; white-space: normal; color: #999;" data-img-style="float:left;margin-right:15px;width:90px;height:60px;" data-is-handling="1">
nginx学习,看这一篇就够了:下载、安装。使用:正向代理、反向代理、负载均衡。常用命令和配置文件
10-25 阅读数 4万+
文章目录前言一、nginx简介1. 什么是 nginx 和可以做什么事情2.Nginx 作为 web 服务器3. 正向代理4. 反向代理5. 动静分离6.动静分离二、Nginx 的安装三、 Ngin... 博文 来自: 冯安晨
ALSA(高级Linux声音架构):一 简单例子
08-12 阅读数 7900
ALSA是Advanced Linux Sound Architecture,高级Linux声音架构的简称,它在Linux操作系统上提供了音频和MIDI(Musical Instrument Digi... 博文 来自: 旗点云
10 个最难回答的 Java 问题
08-27 阅读数 2万+
1.为什么等待和通知是在 Object 类而不是 Thread 中声明的?一个棘手的 Java 问题,如果 Java编程语言不是你设计的,你怎么能回答这个问题呢。Java编程的常识和深入了解有助于回答... 博文 来自: aaa13268的博客
Linux ALSA音频框架分析二:linux音频子系统介绍
10-09 阅读数 1052
linux音频子系统介绍 Linux音频系统比较复杂,各层间有很多交叉,可能是最无序的子系统,并且它有两套音频驱动框架: OSS (Open Sound System)和ALSA (Advance... 博文 来自: xiaohouye的专栏
面试官:你连RESTful都不知道我怎么敢要你?
11-06 阅读数 10万+
干货,2019 RESTful最贱实践 博文 来自: dotNet全栈开发
<div class="recommend-item-box recommend-recommend-box"><div id="kp_box_65" data-pid="65"><script async="async" charset="utf-8" src="https://shared.ydstatic.com/js/yatdk/3.0.1/stream.js" data-id="8935aa488dd58452b9e5ee3b44f1212f" data-div-style="width:100%;" data-tit-style="margin-bottom: 6px; font-size: 18px; line-height: 24px; color: #3d3d3d;display: inline-block;font-weight:bold;" data-des-style="font-size: 13px; line-height: 22px; white-space: normal; color: #999;" data-img-style="float:left;margin-right:15px;width:90px;height:60px;" data-is-handling="1">
ALSA音频架构之声卡设备信息
08-23 阅读数 251
最近项目中遇到了一个蓝牙和语音同时占用一个录音设备的问题。报错打印如下:audio_hw_primary: cannot open pcm_cap: cannot open device ‘/dev/... 博文 来自: Sunxiaolin2016的博客
白话阿里巴巴Java开发手册高级篇
09-28 阅读数 386
作者/分享人:李艳鹏不久前,阿里巴巴发布了《阿里巴巴Java开发手册》,总结了阿里巴巴内部实际项目开发过程中开发人员应该遵守的研发流程规范,这些流程规范在一定程度上能够保证最终的项目交付质量,通过在时... 博文 来自: Arthur0923的博客
Python 基础(一):入门必备知识
10-30 阅读数 9万+
Python 入门必备知识,你都掌握了吗? 博文 来自: 程序之间
redis分布式锁,面试官请随便问,我都会
11-10 阅读数 1万+
文章有点长并且绕,先来个图片缓冲下!前言现在的业务场景越来越复杂,使用的架构也就越来越复杂,分布式、高并发已经是业务要求的常态。像腾讯系的不少服务,还有CDN优化、异地多备份等处理。说到分布式,就必然... 博文 来自: 公众号-[程序员之道]
linux alsa 混音器 以及使用任意波特率录音
11-06 阅读数 466
linux 混音器 以及 使用任意波特率录音使用混音器的原因:直接使用声卡只能一个程序播放声音,如果使用多个程序同时播放则需要使用混音器,使用混音器可在 用户 HOME 文件夹下创建 .asoundr... 博文 来自: jionfull的博客
<div class="recommend-item-box recommend-recommend-box"><div id="kp_box_66" data-pid="66"><script type="text/javascript">
(function() {
var s = "_" + Math.random().toString(36).slice(2);
document.write('<div style="" id="' + s + '"></div>');
(window.slotbydup = window.slotbydup || []).push({
id: "u4623747",
container: s
});
})();
Python——画一棵漂亮的樱花树(不同种樱花+玫瑰+圣诞树喔)
10-22 阅读数 15万+
最近翻到一篇知乎,上面有不少用Python(大多是turtle库)绘制的树图,感觉很漂亮,我整理了一下,挑了一些我觉得不错的代码分享给大家(这些我都测试过,确实可以生成) one 樱花树
动…
博文
大学四年自学走来,这些私藏的实用工具/学习网站我贡献出来了
01-12 阅读数 26万+
大学四年,看课本是不可能一直看课本的了,对于学习,特别是自学,善于搜索网上的一些资源来辅助,还是非常有必要的,下面我就把这几年私藏的各种资源,网站贡献出来给你们。主要有:电子书搜索、实用工具、在线视频... 博文
python学习方法总结(内附python全套学习资料)
11-03 阅读数 2万+
不要再问我python好不好学了 我之前做过半年少儿编程老师,一个小学四年级的小孩子都能在我的教学下独立完成python游戏,植物大战僵尸简单版,如果要肯花时间,接下来的网络开发也不是问题,人工智能也... 博文
兼职程序员一般可以从什么平台接私活?
10-31 阅读数 15万+
这个问题我进行了系统性的总结,以下将进行言简意赅的说明和渠道提供,希望对各位小猿/小媛们有帮助~
根据我们的经验,程序员兼职主要分为三种:兼职职位众包、项目整包和自由职业者驻场。
所谓的兼职职位众…
博文
【排序】插入类排序—(折半)插入排序、希尔排序
11-15 阅读数 2520
前言 在数据结构和算法中,排序是非常重要的一环,并且排序也是渗透编程的方方面面。 你或许在写一个sql的order by按照某组进行排序,又或者你在刷一道题时候、常常遇到贪心+自定义排序求解的思路... 博文
<div class="recommend-item-box recommend-recommend-box"><div style="width: 100%;"><iframe width="962" frameborder="0" height="52" scrolling="no" src="//pos.baidu.com/s?hei=52&wid=962&di=u3491668&ltu=https%3A%2F%2Fblog.csdn.net%2Fzyuanyun%2Farticle%2Fdetails%2F59180272&psi=d30d5658233dfdd482bfdceb3896f6bb&pis=-1x-1&cec=UTF-8&dtm=HTML_POST&tpr=1580381291260&tcn=1580381291&psr=1366x768&col=zh-CN&pcs=1349x635&par=1366x728&ti=Linux%20ALSA%20%E9%9F%B3%E9%A2%91%E7%B3%BB%E7%BB%9F%EF%BC%9A%E9%80%BB%E8%BE%91%E8%AE%BE%E5%A4%87%E7%AF%87&exps=111000,110011&dis=0&ari=2&pss=1349x26259&dc=3&ant=0&cdo=-1&cja=false&cce=true&ccd=24&ltr=https%3A%2F%2Fblog.csdn.net%2Fzyuanyun%3Ft%3D1&prot=2&dai=3&drs=1&cfv=0&tlm=1580381291&cmi=47&cpl=27&chi=1&dri=1&ps=23356x380"></iframe></div><script type="text/javascript" src="//rabc1.iteye.com/production/res/rxjg.js?pkcgstj=jm"></script></div>
Python十大装B语法
11-02 阅读数 24万+
Python 是一种代表简单思想的语言,其语法相对简单,很容易上手。不过,如果就此小视 Python 语法的精妙和深邃,那就大错特错了。本文精心筛选了最能展现 Python 语法之精妙的十个知识点,并... 博文
2019年11月中国大陆编程语言排行榜
11-02 阅读数 5万+
2019年11月2日,我统计了某招聘网站,获得有效程序员招聘数据9万条。针对招聘信息,提取编程语言关键字,并统计如下: 编程语言比例
rank
pl_
percentage
1
jav…
博文
C++知识点 —— 整合(持续更新中)
11-03 阅读数 9618
本文记录自己在自学C++过程中不同于C的一些知识点,适合于有C语言基础的同学阅读。如果纰漏,欢迎回复指正
目录
第一部分 基础知识
一、HelloWorld与命名空间
二、引用和引用参数
2…
博文
《奇巧淫技》系列-python!!每天早上八点自动发送天气预报邮件到QQ邮箱
01-19 阅读数 2万+
将代码部署服务器,每日早上定时获取到天气数据,并发送到邮箱。 也可以说是一个小型人工智障。 知识可以运用在不同地方,不一定非是天气预报。... 博文
Python实例大全(基于Python3.7.4)
11-04 阅读数 1万+
博客说明: 这是自己写的有关python语言的一篇综合博客。 只作为知识广度和编程技巧学习,不过于追究学习深度,点到即止、会用即可。 主要是基础语句,如三大控制语句(顺序、分支、循环),随机数的... 博文
<div class="recommend-item-box recommend-recommend-box"><div id="_f1squ7ipibw" style=""><iframe width="962" frameborder="0" height="52" scrolling="no" src="https://pos.baidu.com/s?hei=52&wid=962&di=u3491668&ltu=https%3A%2F%2Fblog.csdn.net%2Fzyuanyun%2Farticle%2Fdetails%2F59180272&psi=d30d5658233dfdd482bfdceb3896f6bb&pss=1349x26317&ltr=https%3A%2F%2Fblog.csdn.net%2Fzyuanyun%3Ft%3D1&prot=2&pcs=1349x635&cja=false&cdo=-1&exps=111000,110011&tlm=1580381291&cce=true&ti=Linux%20ALSA%20%E9%9F%B3%E9%A2%91%E7%B3%BB%E7%BB%9F%EF%BC%9A%E9%80%BB%E8%BE%91%E8%AE%BE%E5%A4%87%E7%AF%87&ant=0&dri=2&dc=3&cec=UTF-8&cmi=47&dis=0&cpl=27&tpr=1580381291260&par=1366x728&psr=1366x768&tcn=1580381291&ps=23824x380&ari=2&pis=-1x-1&drs=1&chi=1&cfv=0&ccd=24&dai=4&col=zh-CN&dtm=HTML_POST"></iframe></div><script type="text/javascript" src="//rabc1.iteye.com/production/res/rxjg.js?pkcgstj=jm"></script></div>
腾讯算法面试题:64匹马8个跑道需要多少轮才能选出最快的四匹?
11-05 阅读数 6万+
昨天,有网友私信我,说去阿里面试,彻底的被打击到了。问了为什么网上大量使用ThreadLocal的源码都会加上private static?他被难住了,因为他从来都没有考虑过这个问题。无独有偶,今天笔... 博文
论文读不懂怎么办?
11-05 阅读数 4852
王树义读完需要18分钟速读仅需6分钟悄悄告诉你几个窍门。1 痛苦做科研,不能不读论文。但是,我见过不少研究生,论文都读得愁眉苦脸的。这其中,自然有因为拖延的关系。例如教授布置了2周后讨论论文,你原本可... 博文
Hadoop技术(四)分布式、面向列的开源数据库HBase
11-09 阅读数 1763
面向列的据库HBase 第一章 Hbase介绍Hadoop生态系统图非关系型数据库知识面扩展HBase简介HBase数据模型HBase架构 第一章 Hbase介绍
本阶段介绍HBase 是一个分布…
博文
刷了几千道算法题,这些我私藏的刷题网站都在这里了!
11-08 阅读数 7万+
遥想当年,机缘巧合入了 ACM 的坑,周边巨擘林立,从此过上了"天天被虐似死狗"的生活…
然而我是谁,我可是死狗中的战斗鸡,智力不够那刷题来凑,开始了夜以继日哼哧哼哧刷题的日子,从此"读题与提交…
博文
项目中的if else太多了,该怎么重构?
11-11 阅读数 12万+
介绍 最近跟着公司的大佬开发了一款IM系统,类似QQ和微信哈,就是聊天软件。我们有一部分业务逻辑是这样的 if (msgType = "文本") { // dosomething } else if... 博文
致 Python 初学者
11-13 阅读数 17万+
欢迎来到“Python进阶”专栏!来到这里的每一位同学,应该大致上学习了很多 Python 的基础知识,正在努力成长的过程中。在此期间,一定遇到了很多的困惑,对未来的学习方向感到迷茫。我非常理解你们所... 博文
【C++100问】深入理解理解顶层const和底层const
11-11 阅读数 2187
专栏C++学习笔记 声明 1)该文章整理自网上的大牛和相关专家无私奉献的资料,具体引用的资料请看参考文献。 2)本文仅供学术交流,非商用。所以每一部分具体的参考资料并没有详细对应。如果某部分不小心侵犯... 博文
YouTube排名第一的励志英文演讲《Dream(梦想)》
11-12 阅读数 4万+
Idon’t know what that dream is that you have, I don't care how disappointing it might have been as y... 博文
吐血推荐珍藏的Visual Studio Code插件
11-12 阅读数 1万+
作为一名Java工程师,由于工作需要,最近一个月一直在写NodeJS,这种经历可以说是一部辛酸史了。好在有神器Visual Studio Code陪伴,让我的这段经历没有更加困难。眼看这段经历要告一段... 博文
《C++ Primer》学习笔记(五):循环、分支、跳转和异常处理语句
11-18 阅读数 2599
专栏C++学习笔记 《C++ Primer》学习笔记/习题答案 总目录
https://blog.csdn.net/TeFuirnever/article/details/100700212
——…
博文
“狗屁不通文章生成器”登顶GitHub热榜,分分钟写出万字形式主义大作
11-13 阅读数 13万+
一、垃圾文字生成器介绍
最近在浏览GitHub的时候,发现了这样一个骨骼清奇的雷人项目,而且热度还特别高。
项目中文名:狗屁不通文章生成器
项目英文名:BullshitGenerator
根据作…
博文
程序员:我终于知道post和get的区别
11-14 阅读数 21万+
是一个老生常谈的话题,然而随着不断的学习,对于以前的认识有很多误区,所以还是需要不断地总结的,学而时习之,不亦说乎... 博文
《程序人生》系列-这个程序员只用了20行代码就拿了冠军
11-15 阅读数 5万+
你知道的越多,你不知道的越多 点赞再看,养成习惯GitHub上已经开源https://github.com/JavaFamily,有一线大厂面试点脑图,欢迎Star和完善
前言
这一期不算《吊打…
博文
加快推动区块链技术和产业创新发展,2019可信区块链峰会在京召开
11-18 阅读数 6万+
11月8日,由中国信息通信研究院、中国通信标准化协会、中国互联网协会、可信区块链推进计划联合主办,科技行者协办的2019可信区块链峰会将在北京悠唐皇冠假日酒店开幕。
区块链技术被认为是继蒸汽机、…
博文
Python 植物大战僵尸代码实现(2):植物卡片选择和种植
11-23 阅读数 1万+
这篇文章要介绍的是: - 上方植物卡片栏的实现。 - 点击植物卡片,鼠标切换为植物图片。 - 鼠标移动时,判断当前在哪个方格中,并显示半透明的植物作为提示。... 博文
Python3.7黑帽编程——病毒篇(基础篇)
01-24 阅读数 9222
引子 Hacker(黑客),往往被人们理解为只会用非法手段来破坏网络安全的计算机高手。但是,黑客其实不是这样的,真正的“网络破坏者”是和黑客名称和读音相似的骇客。 骇客,是用黑客手段进行非法操作并为己... 博文
程序员把地府后台管理系统做出来了,还有3.0版本!12月7号最新消息:已在开发中有github地址
11-17 阅读数 17万+
第一幕:缘起
听说阎王爷要做个生死簿后台管理系统,我们派去了一个程序员……
996程序员做的梦:
第一场:团队招募
为了应对地府管理危机,阎王打算找“人”开发一套地府后台管理系统,于是…
博文
网易云6亿用户音乐推荐算法
11-17 阅读数 5万+
网易云音乐是音乐爱好者的集聚地,云音乐推荐系统致力于通过 AI 算法的落地,实现用户千人千面的个性化推荐,为用户带来不一样的听歌体验。
本次分享重点介绍 AI 算法在音乐推荐中的应用实践,以及在算法…
博文
大学生活这样过,校招 offer 飞来找
11-19 阅读数 1万+
本篇我们来聊聊大学生活如何度过,才能在校招中拿到 offer。 博文
小白都能看得懂的java虚拟机内存模型
11-26 阅读数 3万+
目录
一、虚拟机
二、虚拟机组成
1.栈
栈帧
2.程序计数器
3.方法区
对象组成
4.本地方法栈
5.堆
GC
GC案例
一、虚拟机
同样的java代码在不…
博文
shell脚本基础
11-21 阅读数 1万+
shell简介:shell是一种脚本语言,可以使用逻辑判断、循环等语法,可以自定义函数,是系统命令的集合 文章目录shell脚本结构和执行方法shell脚本中date命令的用法 shell脚本结构和执... 博文
8年经验面试官详解 Java 面试秘诀
11-19 阅读数 9万+
作者 |胡书敏
责编 | 刘静
出品 | CSDN(ID:CSDNnews)
本人目前在一家知名外企担任架构师,而且最近八年来,在多家外企和互联网公司担任Java技术面试官,前后累计面试了有两三…
博文
面试官如何考察你的思维方式?
11-19 阅读数 5万+
1.两种思维方式在求职面试中,经常会考察这种问题:北京有多少量特斯拉汽车?某胡同口的煎饼摊一年能卖出多少个煎饼?深圳有多少个产品经理?一辆公交车里能装下多少个乒乓球?一个正常成年人有多少根头发?这类估... 博文
碎片化的时代,如何学习
11-20 阅读数 2万+
今天周末,和大家聊聊学习这件事情。 在如今这个社会,我们的时间被各类 APP 撕的粉碎。 刷知乎、刷微博、刷朋友圈; 看论坛、看博客、看公号; 等等形形色色的信息和知识获取方式一个都不错过。 貌似学了... 博文
腾讯“疯狂”开源!
11-20 阅读数 3万+
作者 | 马超
责编 | 胡巍巍
出品 | CSDN(ID:CSDNnews)
近日,腾讯自研的万亿级分布式消息中间件TubeMQ正式开源,并捐赠给Apache基金会,成为基金会官方认可的Inc…
博文
so easy! 10行代码写个"狗屁不通"文章生成器
11-20 阅读数 8万+
前几天,GitHub 有个开源项目特别火,只要输入标题就可以生成一篇长长的文章。
背后实现代码一定很复杂吧,里面一定有很多高深莫测的机器学习等复杂算法
不过,当我看了源代码之后…
博文
知乎高赞:中国有什么拿得出手的开源软件产品?(整理自本人原创回答)
11-20 阅读数 5万+
知乎高赞:中国有什么拿得出手的开源软件产品? 在知乎上,有个问题问“中国有什么拿得出手的开源软件产品(在 GitHub 等社区受欢迎度较好的)?” 事实上,还不少呢~ 本人于2019.7.6进行... 博文
MySQL数据库总结
11-25 阅读数 7万+
一、数据库简介
数据库(Database,DB)是按照数据结构来组织,存储和管理数据的仓库。
典型特征:数据的结构化、数据间的共享、减少数据的冗余度,数据的独立性。
关系型数据库:使用关系模型把数据…
博文
20行Python代码爬取王者荣耀全英雄皮肤
11-21 阅读数 12万+
引言 王者荣耀大家都玩过吧,没玩过的也应该听说过,作为时下最火的手机MOBA游戏,咳咳,好像跑题了。我们今天的重点是爬取王者荣耀所有英雄的所有皮肤,而且仅仅使用20行Python代码即可完成。 准备工... 博文
张小龙-年薪近3亿的微信之父,他是如何做到的?
11-22 阅读数 10万+
张小龙生于湖南邵东魏家桥镇,
家庭主要特点:穷。
不仅自己穷,亲戚也都很穷,可以说穷以类聚。爷爷做过铜匠,总的来说,标准的劳动阶级出身。
家有兄弟两人,
一个小龙,一个小虎。
小虎好动,与邻…
博文
<div class="recommend-item-box type_hot_word">
<div class="content clearfix" style="width: 852px;">
<div class="float-left">
<span>
<a href="https://blog.csdn.net/yilovexing/article/details/80577510" target="_blank">
python</a>
</span>
<span>
<a href="https://blog.csdn.net/slwbcsdn/article/details/53458352" target="_blank">
json</a>
</span>
<span>
<a href="https://blog.csdn.net/csdnnews/article/details/83753246" target="_blank">
java</a>
</span>
<span>
<a href="https://blog.csdn.net/qq_35077512/article/details/88952519" target="_blank">
mysql</a>
</span>
<span>
<a href="https://blog.csdn.net/pdcfighting/article/details/80297499" target="_blank">
pycharm</a>
</span>
<span>
<a href="https://blog.csdn.net/sinyu890807/article/details/97142065" target="_blank">
android</a>
</span>
<span>
<a href="https://blog.csdn.net/gexiaoyizhimei/article/details/100122368" target="_blank">
linux</a>
</span>
<span>
<a href="https://download.csdn.net/download/xhg_gszs/10978826" target="_blank">
json格式</a>
</span>
<span>
<a href="https://www.csdn.net/gather_10/OtTakg2sLWRvd25sb2Fk.html" target="_blank">
c#交错数组</a>
</span>
<span>
<a href="https://www.csdn.net/gather_1e/OtTakg3sLWRvd25sb2Fk.html" target="_blank">
c# task停止</a>
</span>
<span>
<a href="https://www.csdn.net/gather_19/OtTakg4sLWRvd25sb2Fk.html" target="_blank">
c#使用mongodb</a>
</span>
<span>
<a href="https://www.csdn.net/gather_1b/OtTakg5sLWRvd25sb2Fk.html" target="_blank">
c#入门经典第七版</a>
</span>
<span>
<a href="https://www.csdn.net/gather_1a/MtTaAgwsMC1kb3dubG9hZAO0O0OO0O0O.html" target="_blank">
c#设置超时程序</a>
</span>
<span>
<a href="https://www.csdn.net/gather_1b/MtTaAgwsMS1kb3dubG9hZAO0O0OO0O0O.html" target="_blank">
c#一个日期格式加上时分</a>
</span>
<span>
<a href="https://www.csdn.net/gather_1f/MtTaAgwsMi1kb3dubG9hZAO0O0OO0O0O.html" target="_blank">
c# 按行读取excel</a>
</span>
<span>
<a href="https://www.csdn.net/gather_1a/MtTaAgwsMy1kb3dubG9hZAO0O0OO0O0O.html" target="_blank">
c#画图固定</a>
</span>
<span>
<a href="https://www.csdn.net/gather_1f/MtTaAgwsNC1kb3dubG9hZAO0O0OO0O0O.html" target="_blank">
c# 读取dataset</a>
</span>
<span>
<a href="https://www.csdn.net/gather_22/MtTaAgwsNS1ibG9n.html" target="_blank">
如何c#按钮透明</a>
</span>
</div>
</div>
</div>
<div class="recommend-loading-box">
<img src="https://csdnimg.cn/release/phoenix/images/feedLoading.gif">
</div>
<div class="recommend-end-box" style="display: block;">
<p class="text-center">没有更多推荐了,<a href="https://blog.csdn.net/" class="c-blue c-blue-hover c-blue-focus">返回首页</a></p>
</div>
</div>
<div class="template-box">
<span>©️2019 CSDN</span><span class="point"></span>
<span>皮肤主题: 深蓝海洋</span>
<span> 设计师:
CSDN官方博客 </span>
</div>
</main>
最后
以上就是朴实朋友为你收集整理的(二)Linux ALSA 音频系统:逻辑设备篇Linux ALSA 音频系统:逻辑设备篇6. 声卡和 PCM 设备的建立过程7. Frame & Period的全部内容,希望文章能够帮你解决(二)Linux ALSA 音频系统:逻辑设备篇Linux ALSA 音频系统:逻辑设备篇6. 声卡和 PCM 设备的建立过程7. Frame & Period所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。