Linux ALSA详解-程序员宅基地

技术标签: rv1126  linux  音频编码解码  

Linux ALSA详解

1. 介绍

ALSA(即Advanced Linux Sound Architecture), 是目前Linux的主流音频体系结构, 提供了音频和MIDI的支持, 其架构图如下所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g0pGILrT-1628238732044)(C:%5CUsers%5CDELL%5CDesktop%5C%E7%AC%94%E8%AE%B0%5C%E6%B7%BB%E5%8A%A0%E8%AE%BE%E5%A4%87%E8%AE%B0%E5%BD%95.assets%5C190107091242141.png)]

TIP: 笔者的代码分析基于linux-4.14.19

2. 初始化

系统启动中ALSA初始化过程如下

alsa_sound_init()
  /* 注册alsa字符设备 */
  register_chrdev(116, "alsa", &snd_fops)
  /* 创建/proc/asound目录及下属version、devices、cards、modules等文件 */
  snd_info_init()
  
const struct file_operations snd_fops =
{
    .owner =    THIS_MODULE,
    .open =     snd_open,
    .llseek =   noop_llseek,
};

从用户空间打开PCM设备过程如下

snd_pcm_open("default", SND_PCM_STREAM_PLAYBACK)  // alsa-lib接口
  open("/dev/snd/controlC0")         // 打开控制设备; 主设备116, 次设备0
  open("/dev/snd/pcmC0D0p")          // 打开PCM设备; 主设备116, 次设备16
    snd_open()                       // 根据主设备号找到该入口
      snd_minors[minor]              // 根据次设备号找到对应操作集
        snd_ctl_f_ops::open()        // 控制设备打开方法
          snd_ctl_open()
        snd_pcm_f_ops::open()        // PCM设备打开方法
          snd_pcm_playback_open()
            snd_lookup_minor_data()  // 根据次设备号查找对应PCM设备(snd_pcm)
            snd_pcm_open()           // 打开PCM播放子流

3. 核心层

核心层为用户空间提供逻辑设备接口, 同时为驱动提供接口来驱动硬件设备, 主要位于sound/core目录下

3.1 数据结构

该层包含的主要数据结构包括

- snd_card      表示一个声卡实例, 包含多个声卡设备
- snd_device    表示一个声卡设备部件
- snd_pcm       表示一个PCM设备, 声卡设备的一种, 用于播放和录音
- snd_control   表示Control设备, 声卡设备的一种, 用于控制声卡
- snd_pcm_str   表示PCM流, 分为playback和capture
- snd_pcm_substream PCM子流, 用于音频的播放或录制
- snd_pcm_ops   PCM流操作集

各结构体之间主要关系图如下所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kbWdtZSs-1628238732047)(Linux%20ALSA%E8%AF%A6%E8%A7%A3.assets/19010709129583.png)]

snd_card主要字段如下

struct snd_card {
    int number;             /* 索引 */
    char id[16];            /* 标识符 */

    char driver[16];        /* 驱动名称 */
    char shortname[32];     /* 短名 */
    char longname[80];      /* 名字 */

    void *private_data;     /* 声卡私有数据*/
    void (*private_free) (struct snd_card *); /* 私有数据释放回调 */

    struct list_head devices;     /* 该声卡下所有设备*/
    struct list_head controls;    /* 该声卡下所有控制设备*/

    struct list_head files_list;  /* 声卡管理文件 */
    struct device *dev;           /* 声卡相关的device */
    struct device card_dev;       /* 用于sysfs, 代表该声卡 */
    bool registered;              /* 是否注册标记 */
};

snd_device主要字段如下

struct snd_device {
    struct list_head list;        /* 所有注册的声卡设备链表 */
    struct snd_card *card;        /* 设备所属声卡 */
    enum snd_device_state state;  /* 设备状态*/
    enum snd_device_type type;    /* 设备类型*/
    void *device_data;            /* 指向具体的声卡设备, 如snd_pcm */
    struct snd_device_ops *ops;   /* 设备操作集*/
};

snd_pcm主要字段如下

struct snd_pcm {
    struct snd_card *card;   /* 该PCM设备所属声卡*/
    struct list_head list;   /* 所有注册的PCM设备链表 */
    int device;              /* PCM索引 */
    unsigned int info_flags; /* SNDRV_PCM_INFO_ */
    char id[64];             /* PCM设备标识 */
    char name[80];           /* PCM设备名 */
    struct snd_pcm_str streams[2];  /* 指向PCM设备的capture(1)和playback(0)流 */
    void *private_data;      /* PCM设备私有数据*/
    void (*private_free) (struct snd_pcm *); /* 私有数据释放回调 */
};
3.2 接口

该层主要接口如下

/* 创建和初始化声卡结构体 */
int snd_card_new(struct device *parent, int idx, const char *xid, struct module *, int extra_size, struct snd_card **card_ret);
/* 释放声卡结构体 */
int snd_card_free(struct snd_card * card);
/* 注册声卡 */
int snd_card_register(struct snd_card * card);

/* 创建声卡设备部件, 通常由snd_pcm_new和snd_card_new自动完成 */
int snd_device_new(struct snd_card *, enum snd_device_type type, void *device_data, struct snd_device_ops *ops);
/* 注册声卡设备部件, 通常由snd_card_register自动完成 */
int snd_device_register(struct snd_card *card, void *device_data);

/* 创建PCM设备 */
int snd_pcm_new(struct snd_card *, const char *id, int device, int playback_count, int capture_count, struct snd_pcm **rpcm);
/* 创建PCM流, 通常snd_pcm_new会自动创建capture和playback两个PCM流 */
int snd_pcm_new_stream(struct snd_pcm * pcm, int stream, int substream_count);
/* 设置PCM设备操作集 */
void snd_pcm_set_ops(struct snd_pcm *, int direction, const struct snd_pcm_ops *ops);

snd_card_new完成了如下事宜
1. 分配snd_card+extra_size空间大小
2. 如果extra_size大于0,将private_data指向extra_size所在首地址
3. 如果指定了xid, 将其拷贝至snd_card::id中, 即声卡标识符
4. 根据idx获取可用的声卡索引并赋值给snd_card::number
5. 分别将parent、module赋值给snd_card::dev、snd_card::module
6. 初始化链表snd_card::devices、snd_card::controls、snd_card::ctl_files、snd_card::files_list
7. 调用device_initialize()初始化snd_card::card_dev, 并设置snd_card::card_dev相关成员变量, 用于sysfs
8. 调用snd_ctl_create()创建控制接口
8.1 调用snd_device_initialize初始化snd_card::ctl_dev, 并设置相关成员变量, 用于sysfs
8.2 调用snd_device_new(SNDRV_DEV_CONTROL, ops)创建声卡控制设备部件

    static struct snd_device_ops ops = {
        .dev_free = snd_ctl_dev_free,
        .dev_register = snd_ctl_dev_register,
        .dev_disconnect = snd_ctl_dev_disconnect,
    };

9. 调用snd_info_card_create()创建proc对应文件系统

snd_card_register完成了如下事宜
1. 如果声卡未注册(snd_card::registered), 调用device_add(snd_card::card_dev)将声卡添加到sysfs
2. 调用snd_device_register_all(snd_card)注册该声卡下所有声卡设备(即snd_card::devices链表), 即完成snd_device_register相同的功能
2.1 遍历snd_card::devices链表, 依次调用__snd_device_register注册声卡设备
2.1.1 调用snd_device::snd_device_ops::dev_register注册该设备, 对于Control设备, 即snd_ctl_dev_register; 对于PCM设备, 即snd_pcm_dev_register; 最终则都会调用snd_register_device
2.1.1.1 snd_ctl_dev_register: 调用snd_register_device(snd_ctl_f_ops)注册该Control设备
2.1.1.2 snd_pcm_dev_register: 调用snd_pcm_add将该PCM设备添加至全局PCM链表snd_pcm_devices中, 然后调用snd_register_device(snd_pcm_f_ops)注册该PCM设备
2.1.1.x.1 snd_register_device: 分配snd_minor空间, 设置type、card、device、f_ops、card_ptr等成员变量; 通过snd_find_free_minor找到合适的minor并通过MKDEV(116, minor)创建设备节点, 然后通过device_add向系统添加该设备; 最后将该声卡设备添加至全局声卡主设备的次设备数组snd_minors中
3. 将该声卡放入全局静态声卡数组snd_cards中
4. 调用init_info_for_card()向proc文件系统注册该声卡

snd_pcm_new完成了如下事宜
1. 分配snd_pcm空间, 并设置snd_pcm::card、snd_pcm::device等成员变量
2. 调用snd_pcm_new_stream(SNDRV_PCM_STREAM_PLAYBACK)创建playback_count个子流用于播放
3. 调用snd_pcm_new_stream(SNDRV_PCM_STREAM_CAPTURE)创建capture_count个子流用于录制
4. 调用snd_device_new(SNDRV_DEV_PCM, ops)添加PCM设备

    static struct snd_device_ops ops = {
        .dev_free = snd_pcm_dev_free,
        .dev_register = snd_pcm_dev_register,
        .dev_disconnect = snd_pcm_dev_disconnect,
    };

snd_pcm_new_stream完成了如下事宜
1. 设置snd_pcm::stream[playback or catpure]对应stream、pcm、substream_count成员变量
2. 调用snd_device_initialize()初始化snd_pcm::stream::dev, 并设置相关成员变量, 用于sysfs
3. 调用snd_pcm_stream_proc_init(snd_pcm_str)初始化对应proc文件系统
4. 分配substream_count个snd_pcm_substream并进行相应初始化

3.3 实现

核心驱动的一般实现步骤如下
\1. 调用snd_card_create创建声卡实例(struct snd_card)
\2. 定义声卡的私有结构体用于存放该声卡的一些资源信息, 如中断资源、IO资源、DMA资源等
\3. 硬件初始化, 包括数字音频接口初始化、DMA控制器初始化、编解码器初始化
\4. 调用snd_pcm_new创建逻辑设备, 并实现其操作集snd_pcm_ops
\5. 调用snd_card_register注册声卡实例及声卡设备

具体实例可参考sound/atmel/ac97csound/spi/at73c213的实现

4. ASOC层

在移动设备中, 为了更好的提供ALSA支持, 在核心层的基础上出现了ASOC(ALSA System on Chip)层
ASOC层代码位于sound/soc/*, 主要由如下三部分组成
- Codec: 负责配置编解码器提供音频捕获和回放功能
- Platform: 主要负责SoC平台音频DMA和音频接口的配置和控制, 包括时钟、DMA、I2S、PCM等
- Machine: Codec、Platform、输入输出设备提供了一个载体

4.1 DAI

DAI(Digital Audio Interfaces), 即 数字音频接口
ASOC支持三种主流DAI: AC97、I2S和PCM

AC97: 通常用于PC声卡, 为5线接口, 每个AC97帧为21uS长, 被分为13个时隙
- BCLK: 由AC97驱动, 为12.288 MHz
- SYNC: 同步信号, 由Controler驱动, 为48 kHz
- SDATDIN: 用于capture, AC97->Controler
- SDATAOUT: 用于playback, Controler->AC97
- RESET: 由Controler生成, 用于唤醒AC97

I2S是HiFi、STB和便携式设备中常用的4线DAI
- SCLK: 串行时钟
- LRCK: 也称WS, 声道选择线
- Tx: 用于传输音频数据
- Rx: 用于接收音频数据

PCM是另一种4线接口, 与I2S非常相似, 可以支持更灵活的协议
- BCLK: 位时钟, 根据采样率而变化
- SYNC: 同步信号
- Tx: 用于传输音频数据
- Rx: 用于接收音频数据

4.2 Codec

Codec驱动应该实现为通用与硬件无关的,用于配置编解码器、FM、MODEM、BT或外部DSP, 以提供playback和capture, 这部分代码通常位于sound/soc/codecs/*

每个Codec驱动必须��供如下功能
\1. Codec DAI和PCM配置
\2. 使用RegMap实现的Codec控制IO
\3. Mixers和Audio控制
\4. Codec音频操作
\5. DAPM描述
\6. DAPM事件处理
\7. DAC静音控制(可选)

4.2.1 数据结构

Codec层主要结构体包括snd_soc_codec、snd_soc_codec_driver、snd_soc_dai、snd_soc_dai_driver

snd_soc_codec代表一个Codec设备, 其主要字段如下

struct snd_soc_codec {
    struct device *dev;        /* 指向Codec设备的指针 */
    const struct snd_soc_codec_driver *driver;  /* 该Codec对应的驱动 */
    struct list_head list;

    /* runtime */
    unsigned int cache_init:1; /* 指示Codec cache是否初始化 */

    /* codec IO */
    void *control_data;        /* 控制IO数据 */
    hw_write_t hw_write;       /* 控制IO函数 */
    void *reg_cache;

    /* component */
    struct snd_soc_component component;
};

snd_soc_codec_driver代表一个Codec驱动, 其主要字段如下

struct snd_soc_codec_driver {
    /* 操作集 */
    int (*probe)(struct snd_soc_codec *);
    int (*remove)(struct snd_soc_codec *);
    int (*suspend)(struct snd_soc_codec *);
    int (*resume)(struct snd_soc_codec *);
    struct snd_soc_component_driver component_driver;

    /* codec wide operations */
    int (*set_sysclk)(struct snd_soc_codec *codec,
              int clk_id, int source, unsigned int freq, int dir);
    int (*set_pll)(struct snd_soc_codec *codec, int pll_id, int source,
        unsigned int freq_in, unsigned int freq_out);
    int (*set_jack)(struct snd_soc_codec *codec,
            struct snd_soc_jack *jack,  void *data);

    /* Codec IO相关函数 */
    struct regmap *(*get_regmap)(struct device *);
    unsigned int (*read)(struct snd_soc_codec *, unsigned int);
    int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
    
    /* 偏置电压配置函数 */
    int (*set_bias_level)(struct snd_soc_codec *,
                  enum snd_soc_bias_level level);
};

snd_soc_dai代表DAI运行时数据, 其主要字段如下

struct snd_soc_dai {
    const char *name;    /* 名称 */
    int id;              /* 索引 */
    struct device *dev;  /* DAI设备 */

    /* 驱动操作集 */
    struct snd_soc_dai_driver *driver;

    /* DAI运行时信息 */
    unsigned int capture_active:1;
    unsigned int playback_active:1;
    unsigned int symmetric_rates:1;
    unsigned int symmetric_channels:1;
    unsigned int symmetric_samplebits:1;
    unsigned int probed:1;

    unsigned int active;

    struct snd_soc_dapm_widget *playback_widget;
    struct snd_soc_dapm_widget *capture_widget;

    /* DAI DMA data */
    void *playback_dma_data;  /* 用于管理playback DMA */
    void *capture_dma_data;   /* 用于管理capture DMA */

    /* Symmetry data - only valid if symmetry is being enforced */
    unsigned int rate;
    unsigned int channels;
    unsigned int sample_bits;

    /* parent platform/codec */
    struct snd_soc_codec *codec;           /* 绑定的Codec */
    struct snd_soc_component *component;   /* 绑定的platform */

    struct list_head list;
};

snd_soc_dai_driver代表一个DAI驱动, 其主要字段如下

struct snd_soc_dai_driver {
    /* DAI描述 */
    const char *name;
    unsigned int id;
    unsigned int base;
    struct snd_soc_dobj dobj;

    /* DAI驱动回调 */
    int (*probe)(struct snd_soc_dai *dai);
    int (*remove)(struct snd_soc_dai *dai);
    int (*suspend)(struct snd_soc_dai *dai);
    int (*resume)(struct snd_soc_dai *dai);
    /* compress dai */
    int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num);
    /* Optional Callback used at pcm creation*/
    int (*pcm_new)(struct snd_soc_pcm_runtime *rtd,
               struct snd_soc_dai *dai);
    /* DAI is also used for the control bus */
    bool bus_control;

    /* 操作集 */
    const struct snd_soc_dai_ops *ops;
    const struct snd_soc_cdai_ops *cops;

    /* DAI能力 */
    struct snd_soc_pcm_stream capture;
    struct snd_soc_pcm_stream playback;
    unsigned int symmetric_rates:1;
    unsigned int symmetric_channels:1;
    unsigned int symmetric_samplebits:1;

    /* probe ordering - for components with runtime dependencies */
    int probe_order;
    int remove_order;
};
4.2.2 接口
int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *, struct snd_soc_dai_driver *, int num_dai);

snd_soc_register_codec完成了如下事宜
1. 分配snd_soc_codec空间
2. 调用snd_soc_component_initialize()初始化snd_soc_codec::snd_soc_component
3. snd_soc_codec::snd_soc_component操作集初始化
4. DAPM相关初始化
5. 调用snd_soc_register_dais()注册num_dai个DAI
6. 将该Codec添加至全局Codec链表codec_list中

4.2.3 实现

Codec的一般实现步骤如下
\1. 获取Codec设备资源
\2. 实现snd_soc_codec_driver结构体
\3. 实现snd_soc_dai_driver结构体
\4. 实现snd_soc_dai_ops结构体, 并赋值给snd_soc_dai_driver::ops
\5. 调用snd_soc_register_codec()注册Codec

4.3 Platform

Platform驱动可分为三个部分: 音频DMA驱动、SoC DAI驱动和DSP驱动
这些驱动代码应该只和SoC CPU有关而和Board无关

FIXME: Later

4.4 Machine

Machine/Board驱动用来将所有的部件驱动(Codecs、Platforms和DAIs)进行关联

Audio 框架

表 1-1 SOUND 代码构成

项目 功能 路径
Sound soc 主要包含公共部分代码,包括 dapm 控制, jack, dmaengine, core 等等 sound/soc/
rockchip platform Rockchip 平台的 cpu dai 的驱动, 比如 I²S, spdif 等以及自定义声卡 machine driver sound/soc/rockchip
generic platform simple card framework sound/soc/generic
codec driver 所有的 codec driver 存放位置 sound/soc/codecs

一个声卡包含 cpu_dai, codec_dai, 以及 dai_link 组成,分别对应 cpu dai 的 dirver,比如I²S driver, spdif driver; codec driver, 比如rt5640 codec driver; dai_link driver,也就是 machine driver, 比如 sound/soc/rockchip/rockchip_rt5640.c。 4.4 的内核中支持两种方式创建声卡,一种是通用的 simple-card framework,一种是传统的编写自定义的 machine driver 来创建

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xYLIZH1U-1628238732049)(Linux%20ALSA%E8%AF%A6%E8%A7%A3.assets/fc14b1f06e7a03bb165e0c1b0688b7c.png)]

CODEC DAI

1.xxx_i2c_driver 里面寄生 codec_driver

2.coder_driver 里面注册 dai_driver

参考rk文档 docs/Kernel/Audio

驱动实现步骤

1.修改codec的驱动源码

2.选择通用machine层驱动或是按自己的需求搞一个驱动

3.添加设备信息

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
 */

#include "rv1126-evb-v13.dtsi"
 / {
       es7243_sound: es7243-sound {
                status = "okay";
                compatible = "simple-audio-card";
                simple-audio-card,name = "rockchip,es7243";
                simple-audio-card,format = "i2s";
                simple-audio-card,mclk-fs = <256>;
                simple-audio-card,cpu {
                        sound-dai = <&i2s0_8ch>;
                };
                simple-audio-card,codec {
                        sound-dai = <&es7243_0
				&es7243_1
				&es7243_2
				&es7243_3>;
                };
        };
};

//Dts 的 codec 的i2c部分:
//Dts 的 platform 的 i2s 部分:
//i2c adapter3 信息
&i2c3{
	clock-frequency = <400000>;//设置传输速率 400k
	status = "okay";
	
    	es7243_0:	es7243_0@10{
		#sound-dai-cells = <0>;
		compatible = "MicArray_0";
		reg = <0x10>;
		
                clocks = <&cru MCLK_I2S0_RX>;//?还不知道这个时钟要怎么选,明天看看sdk关于i2c的文档怎么搞
                clock-names = "mclk";
				realtek,in1-differential; //?这是什么意思,没有搞懂啊
				
		};
    	es7243_1:	es7243_1@11{
		#sound-dai-cells = <0>;
		compatible = "MicArray_1";
		reg = <0x11>;
		
                clocks = <&cru MCLK_I2S0_RX>;
                clock-names = "mclk";
				realtek,in1-differential;
		};
    	es7243_2:	es7243_2@13{
		#sound-dai-cells = <0>;
		compatible = "MicArray_2";
		reg = <0x13>;
		
                clocks = <&cru MCLK_I2S0_RX>;
                clock-names = "mclk";
				realtek,in1-differential;
		};
    	es7243_3:	es7243_3@12{
		#sound-dai-cells = <0>;
		compatible = "MicArray_3";
		reg = <0x12>;
		
                clocks = <&cru MCLK_I2S0_RX>;
                clock-names = "mclk";
				realtek,in1-differential;
		};		
	
    };
};


4.调试

rv1126 驱动12c调试信息路径:/sys/devices/platform/ff520000.i2c/i2c-3/3-0010/es7243_debug(用到i2c几就去找相应的i2c路径)

寄存器调试:
驱动提供了寄存器的读写调试,路径 /sys/devices/platform/ff110000.i2c/i2c-1/1-0013/es7243_debug

读例子:
//读取0x00开始的16个寄存器
#echo 0010 > es7243

echo 0010 > es7243

[ 1787.143739] REG[0x00]: 0x01;  REG[0x01]: 0x0c;  REG[0x02]: 0x10;  REG[0x03]: 0x04;  
[ 1787.146729] REG[0x04]: 0x02;  REG[0x05]: 0x13;  REG[0x06]: 0x00;  REG[0x07]: 0x80;  
[ 1787.149371] REG[0x08]: 0x43;  REG[0x09]: 0x3f;  REG[0x0a]: 0xc0;  REG[0x0b]: 0xc0;  
[ 1787.151682] REG[0x0c]: 0x12;  REG[0x0d]: 0xa0;  REG[0x0e]: 0x40;  REG[0x0f]: 0xff;

参考资料:

RK音频相关问题解析

资料

文章源:https://www.linuxidc.com/Linux/2019-01/156223.htm

音频接口资料:http://www.wangdali.net/i2s/

firefly怎么找到codec:http://dev.t-firefly.com/thread-6945-1-1.html

linux音频驱动架构分析:https://blog.csdn.net/mahao1107/article/details/105105832

usb声卡驱动大佬文章:https://blog.csdn.net/xiaowanbiao123

使用ADC芯片ES7243遇到的问题:https://blog.csdn.net/kris_fei/article/details/84838536?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase

https://blog.csdn.net/z2066411585/article/details/108551057?spm=1001.2014.3001.5501

https://blog.csdn.net/kris_fei/article/details/84838536?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase

https://blog.csdn.net/crow_ch/article/details/104879518?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-8.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-8.control

https://blog.csdn.net/zwj695535100/article/details/97973184?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-15.control&dist_request_id=1329188.21126.16179413686966233&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-15.control

一个音频大佬的博客:

https://blog.csdn.net/yangjizhen1533

e-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-8.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-8.control

https://blog.csdn.net/zwj695535100/article/details/97973184?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-15.control&dist_request_id=1329188.21126.16179413686966233&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-15.control

一个音频大佬的博客:

https://blog.csdn.net/yangjizhen1533

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_43209963/article/details/119458372

智能推荐

SpringBoot实现注册操作_springboot不同身份注册-程序员宅基地

文章浏览阅读2.1k次,点赞3次,收藏10次。这段时间开始做后台接口的测试都是在自己电脑上做的一些简单的界面来测试自己写的接口有没有出现什么问题。_springboot不同身份注册

oracle em-application.log 占用空间_oracle中em-application.log-程序员宅基地

文章浏览阅读2.3k次。运行一段时间后,发现磁盘空间剧减,除了数据占用空间外,日志文件也占用空间.如下两个文件占用空间较大home/oracle/rda/outpu/*.loghome/oracle/product/10.2.0/db/oc4j/j2ee/OC4J_DBConsole_gxdb1_gxdb1/log/http-web-access.loghttp-web-access.log_oracle中em-application.log

mini2440 简单按键中断模式驱动程序_make -c $(kern_dir) m=`pwd` modules clean-程序员宅基地

文章浏览阅读853次。MakefileKERN_DIR = /home/grh/kernel_source_code/linux-2.6.32.2all : make -C $(KERN_DIR) M=`pwd` modules arm-linux-gcc key_interrupt_app.c -o key_interrupt_appclean : make -C $(KERN_DIR) M=`pwd_make -c $(kern_dir) m=`pwd` modules clean

subprocess.Popen BrokenPipeError: [Errno 32] Broken pipe-程序员宅基地

文章浏览阅读5.1k次,点赞3次,收藏9次。BrokenPipeError: [Errno 32] Broken pipe在向管道中写入参数的时候,一段时间正常写入数据后,若长时间未写入数据,则会报错BrokenPipeError: [Errno 32] Broken pipe,且该进程会进入睡眠原代码 # 定义管道及ffmpeg命令,输出rtmp流的时候使用command = ['ffmpeg', '-y', '-v', '24', # 日志显示等级 '-f', 'rawv_brokenpipeerror: [errno 32] broken pipe

身价1亿日元的“机器人观音”问世,为普罗大众讲解佛义 ...-程序员宅基地

文章浏览阅读81次。只有人们想不到的,没有机器人做不到的。 日前,日本高台寺展示了智能机器人观音“Minder”,旨在向现代人简单易懂的阐明佛教的教义。 据了解,“Minder”由大阪大学教授石黑浩等人协助研发,研发费用为1亿日元(约600万人民币)。外形方面,“Minder”高约195厘米、重约60公斤,头部、手臂和躯体可以转动,左眼内装有摄像头。 研究..._养猪起家身价1296亿

pandas获得指定行_pandas取dataframe特定行/列_pandas选取特定值所在的行-程序员宅基地

文章浏览阅读1.6w次,点赞13次,收藏96次。转自他人博客:https://blog.csdn.net/weixin_39586825/article/details/1117585061.按列取、按索引/行取、按特定行列取import numpy as npfrom pandas import DataFrameimport pandas as pddf=DataFrame(np.arange(12).reshape((3,4)),index=[‘one’,‘two’,‘thr’],columns=list(‘abcd’))df[‘a’]_pandas选取特定值所在的行

随便推点

php 判断post编码格式,php如何判断get或post的值是否存在-程序员宅基地

文章浏览阅读717次。php如何判断get或post的值是否存在,这个问题困扰了我很长时间,是用isset还是empty还是is_array啥的,请大神给一个指点回复内容:php如何判断get或post的值是否存在,这个问题困扰了我很长时间,是用isset还是empty还是is_array啥的,请大神给一个指点isset()是判断这个变量是否定义,empty()是在这个变量已经定义的情况下(如果变量没定义,将报错var..._isset empty $_get

计算机系统课程 笔记总结 CSAPP第七章 链接(7.1-7.13)_csapp 7.6.1-程序员宅基地

文章浏览阅读2.6k次,点赞16次,收藏45次。GitHub计算机系统CSAPP课程资源 计算机系统课程 笔记总结 CSAPP第二章 信息的表示和处理(2.1-2.2) 计算机系统课程 笔记总结 CSAPP第二章 信息的表示和处理(2.3-2.4) 计算机系统课程 笔记总结 CSAPP第三章 程序的机器级表示(3.2-3.4) 计算机系统课程 笔记总结 CSAPP第三章 程序的机器级表示(3.5-3.7) 计算机系统课程 笔记总结 ..._csapp 7.6.1

Redis总结-程序员宅基地

文章浏览阅读248次。目录概述Redis是什么?简述它的优缺点?为什么要使用Redis/缓存Redis为什么这么快?Redis相比Memcached有哪些优势?Redis的常用场景有哪些?数据类型Redis的数据类型有哪些?线程模型Redis为何选择单线程?Redis真的是单线程?Redisv6.0为何引入多线程?过期键的删除策略Redis过期键的删除策略?过期键的删除策略都有哪些?内存相关MySQL里有2000w数据,redis中只能存20w的数据,如何保证redis中的数据都是热点数据?Redis内存淘汰机制?Redis如何

nginx平滑重启和升级-程序员宅基地

文章浏览阅读77次。平滑重启 kill -HUP `cat /usr/local/www/nginx/logs/nginx.pid`平滑升级nginxcd /yujialinwget http://nginx.org/download/nginx-1.0.6.tar.gztar zxvf nginx-1.0.6.tar.gzcd nginx-1.0.6/usr/local/www/nginx/sbin/nginx -..._php 平滑升级

什么是ip6-程序员宅基地

文章浏览阅读3.9k次。相比于IPv4来说,IPv6的地址更加充足,安全性也更高,可以解决如今IP地址枯竭的问题,为物联网时代的发展提供网络基础;Pv6具有更大的地址空间,使用更小的路由表,加入了对自动配置的支持。ipv6是什么Pv6是第六代互联网协议,被设计用于替代使用了30多年的第四代互联网协议的下一代IP协议。由于IPv4最大的问题在于网络地址资源有限,严重制约了互联网的应用和发展,所以出现了替代版本的IP协议。..._ip6

android bluetooth stack-enable_android bluetooth stack-enable-程序员宅基地

文章浏览阅读2.5k次。android bluetooth stack_android bluetooth stack-enable