六、Audio-ALSA架构中的codec

2023-11-07

一、codec简介

处理器如果既想“听到”外界的声音,又想向外界传达自己的“心声”,那么就需要同时用到 DAC 和 ADC 这两款芯片。那是不是买两颗 DAC 和 ADC 芯片就行了呢?答案肯定是可以的,但是音频不单单是能出声、能听到就行。我们往往需要听到的声音动听、录进去的语音贴近真实、可以调节音效、对声音能够进行一些处理(需要 DSP 单元)、拥有统一的标准接口,方便开发等等。将这些针对声音的各种要求全部叠加到 DAC 和 ADC 芯片上,那么就会得到一个专门用于音频的芯片,也就是音频编解码芯片,英文名字就是 Audio CODEC,所以我们在手机或者电脑的介绍中看到“CODEC”这个词语,一般说的都是音频编解码。

音频 CODEC 的本质是 ADC 和 DAC

二、codec的作用

在移动设备中,Codec的作用可以归结为4种,分别是:

  • 对PCM等信号进行DIA转换,把数字的音频信号转换为模拟信号–播放
  • 对Mic、Linein或者其他输入源的模拟信号进行A/D转换,把模拟的声音信号转变CPU能够处理的数字信号–录音
  • 对音频通路进行控制,比如播放音乐,收听调频收音机,又或者接听电话时,音频信号在codec内的流通路线是不一样的;
  • 对音频信号做出相应的处理,例如音量控制,功率放大,EQ控制等等

ASoC对Codec的这些功能都定义好了一系列相应的接口,以方便地对Codec进行控制。ASoC对Codec驱动的一个基本要求是:驱动程序的代码必须要做到平台无关性,以方便同一个Codec的代码不经修改即可用在不同的平台上。

Linux内核版本:4.1.15
主芯片:IMX6ULL
codec芯片:WM8960

三、codec相关结构体简介

描述Codec的最主要的4个数据结构分别是: snd_soc_codec,snd_soc_codec_driver,snd_soc_dai,snd_soc_dai_driver,其中的snd_soc_dai和snd_soc_dai_driver在ASoC的Platform驱动中也会使用到,Platform和Codec的DAl通过snd_soc_dai_link结构,在Machine驱动中进行绑定连接。下面我们先看看这几个结构的定义,在本文中codec的设备是使用设备树描述的,在这里就不多多说,主要是codec driver,这里我只贴出我要关注的字段。

3.1、snd_soc_codec_driver

代码路径:\include\sound\soc.h
只贴出我要关注的字段

/* codec driver */
struct snd_soc_codec_driver {

	/* driver ops */
	int (*probe)(struct snd_soc_codec *);
	int (*remove)(struct snd_soc_codec *);   	
	int (*suspend)(struct snd_soc_codec *);		/*电源管理*/

	/* Default control and setup, added after probe() is run */
	const struct snd_kcontrol_new *controls;   /*音频控件指针*/
	const struct snd_soc_dapm_widget *dapm_widgets;    /*dapm部件指针*/
	const struct snd_soc_dapm_route *dapm_routes;		 /*dapm路由指针*/

	/* 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);       			/*锁相环配置函数*/

	/* codec IO */
	unsigned int (*read)(struct snd_soc_codec *, unsigned int);     /*读取codec寄存器函数*/
	int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);	/*写入codec寄存器函数*/

	/* codec bias level */
	int (*set_bias_level)(struct snd_soc_codec *, 
			      enum snd_soc_bias_level level);     /*偏置电压配置函数*/
	bool ignore_pmdown_time;  /* Doesn't benefit from pmdown delay */
};

3.2、snd_soc_dai_driver

代码路径:\include\sound\soc-dai.h
重要函数做了些注释。

struct snd_soc_dai_driver {
	/* DAI description */
	const char *name;  /*dai驱动的名字*/

	/* DAI driver callbacks */
	int (*probe)(struct snd_soc_dai *dai);  /*dai驱动的probe函数,由snd_soc_instantiate_card回调*/
	int (*remove)(struct snd_soc_dai *dai);
	int (*suspend)(struct snd_soc_dai *dai);   /*电源管理*/
	int (*resume)(struct snd_soc_dai *dai);
	/* compress dai */
	bool compress_dai;
	/* DAI is also used for the control bus */
	bool bus_control;

	/* ops */
	const struct snd_soc_dai_ops *ops;  /*指向本dai的snd_soc_dai_ops结构*/

	/* DAI capabilities */
	struct snd_soc_pcm_stream capture;    /*描述capture的能力*/
	struct snd_soc_pcm_stream playback;		/*描述playback的能力*/

	/* probe ordering - for components with runtime dependencies */
	int probe_order;
	int remove_order;
};

3.3、struct snd_soc_dai_ops

snd_soc_dai_ops用于实现该dai的控制和配置参数:

代码路径:include\sound\soc-dai.h

struct snd_soc_dai_ops {
	/*
	 * DAI clocking configuration, all optional.
	 * Called by soc_card drivers, normally in their hw_params.
	 */
	int (*set_sysclk)(struct snd_soc_dai *dai,
		int clk_id, unsigned int freq, int dir);   /*设置dai的主时钟;*/
	int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
		unsigned int freq_in, unsigned int freq_out);  /*设置PLL参数;*/
	int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);  /*设置分频系数;*/
	int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio);

	/*
	 * DAI format configuration
	 * Called by soc_card drivers, normally in their hw_params.
	 */
	int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);  /*设置dai的格式;*/
	int (*xlate_tdm_slot_mask)(unsigned int slots,
		unsigned int *tx_mask, unsigned int *rx_mask);
	int (*set_tdm_slot)(struct snd_soc_dai *dai,
		unsigned int tx_mask, unsigned int rx_mask,
		int slots, int slot_width);             /*如果dai支持时分复用,用于设置时分复用的slot*/
	int (*set_channel_map)(struct snd_soc_dai *dai,
		unsigned int tx_num, unsigned int *tx_slot,
		unsigned int rx_num, unsigned int *rx_slot);   /*声道的时分复用映射设置*/
	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);/*设置dai引脚的状态,当与其他dai并联使用同一引脚时需要使用该回调*/

	/*
	 * DAI digital mute - optional.
	 * Called by soc-core to minimise any pops.
	 */
	int (*digital_mute)(struct snd_soc_dai *dai, int mute);
	int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream);

	/*
	 * ALSA PCM audio operations - all optional.
	 * Called by soc-core during audio PCM operations.
	 */
	int (*startup)(struct snd_pcm_substream *,
		struct snd_soc_dai *);   /*打开设备,设备开始工作的时候回调*/
	void (*shutdown)(struct snd_pcm_substream *,
		struct snd_soc_dai *);   /*关闭设备前调用*/
	int (*hw_params)(struct snd_pcm_substream *,
		struct snd_pcm_hw_params *, struct snd_soc_dai *); /*设置硬件的相关参数*/
	int (*hw_free)(struct snd_pcm_substream *,
		struct snd_soc_dai *);
	int (*prepare)(struct snd_pcm_substream *,
		struct snd_soc_dai *);
	/*
	 * NOTE: Commands passed to the trigger function are not necessarily
	 * compatible with the current state of the dai. For example this
	 * sequence of commands is possible: START STOP STOP.
	 * So do not unconditionally use refcounting functions in the trigger
	 * function, e.g. clk_enable/disable.
	 */
	int (*trigger)(struct snd_pcm_substream *, int,
		struct snd_soc_dai *);    /*结束传输,暂停传世,恢复传输的时候被回调*/
	int (*bespoke_trigger)(struct snd_pcm_substream *, int,
		struct snd_soc_dai *);
	/*
	 * For hardware based FIFO caused delay reporting.
	 * Optional.
	 */
	snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
		struct snd_soc_dai *);
};

四、codec的注册

因为Codec驱动的代码要做到平台无关性,要使得Machine驱动能够使用该Codec,Codec驱动的首要任务就是确定snd_soc_codec和snd_soc_dai的实例,并把它们注册到系统中,注册后的codec和 dai才能为Machine驱动所用。以WM8960为例,对应的代码位置:/sound/soc/codecs/wm8960.c,模块的入口函数注册了一个i2c_driver :

static const struct of_device_id wm8960_of_match[] = {
       { .compatible = "wlf,wm8960", },
       { }
};
MODULE_DEVICE_TABLE(of, wm8960_of_match);

static struct i2c_driver wm8960_i2c_driver = {
	.driver = {
		.name = "wm8960",
		.owner = THIS_MODULE,
		.of_match_table = wm8960_of_match,
	},
	.probe =    wm8960_i2c_probe,
	.remove =   wm8960_i2c_remove,
	.id_table = wm8960_i2c_id,
};

这指定了其compatible 属性,与设备树中的compatible 属性就可以调用其probe函数;

设备树中codec实例如下:
在这里插入图片描述

之后就会进入probe函数的流程:

static int wm8960_i2c_probe(struct i2c_client *i2c,
			    const struct i2c_device_id *id)
{
	ret = snd_soc_register_codec(&i2c->dev,
			&soc_codec_dev_wm8960, &wm8960_dai, 1);
}

在这里会注册snd_soc_codec_driver与snd_soc_dai_driver,其实例也在同一个文件中:

snd_soc_codec_driver
在这里插入图片描述

snd_soc_dai_driver
在这里插入图片描述

可见,Codec驱动的第一个步骤就是定义snd_soc_codec_driver和snd_soc_dai_driver的实例,然后调用snd_soc_register_codec函数对Codec进行注册。进入snd_soc_register_codec函数看看:

int snd_soc_register_codec(struct device *dev,
			   const struct snd_soc_codec_driver *codec_drv,
			   struct snd_soc_dai_driver *dai_drv,
			   int num_dai)
{
	struct snd_soc_codec *codec;
	struct snd_soc_dai *dai;
	int ret, i;

	dev_dbg(dev, "codec register %s\n", dev_name(dev));
	
	
	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
	if (codec == NULL)
		return -ENOMEM;

	codec->component.dapm_ptr = &codec->dapm;
	codec->component.codec = codec;

	ret = snd_soc_component_initialize(&codec->component,
			&codec_drv->component_driver, dev);
	if (ret)
		goto err_free;

	if (codec_drv->controls) {
		codec->component.controls = codec_drv->controls;
		codec->component.num_controls = codec_drv->num_controls;
	}
	if (codec_drv->dapm_widgets) {
		codec->component.dapm_widgets = codec_drv->dapm_widgets;
		codec->component.num_dapm_widgets = codec_drv->num_dapm_widgets;
	}
	if (codec_drv->dapm_routes) {
		codec->component.dapm_routes = codec_drv->dapm_routes;
		codec->component.num_dapm_routes = codec_drv->num_dapm_routes;
	}

	if (codec_drv->probe)
		codec->component.probe = snd_soc_codec_drv_probe;
	if (codec_drv->remove)
		codec->component.remove = snd_soc_codec_drv_remove;
	if (codec_drv->write)
		codec->component.write = snd_soc_codec_drv_write;
	if (codec_drv->read)
		codec->component.read = snd_soc_codec_drv_read;
	codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
	codec->dapm.idle_bias_off = codec_drv->idle_bias_off;
	codec->dapm.suspend_bias_off = codec_drv->suspend_bias_off;
	if (codec_drv->seq_notifier)
		codec->dapm.seq_notifier = codec_drv->seq_notifier;
	if (codec_drv->set_bias_level)
		codec->dapm.set_bias_level = snd_soc_codec_set_bias_level;
	codec->dev = dev;
	codec->driver = codec_drv;
	codec->component.val_bytes = codec_drv->reg_word_size;

#ifdef CONFIG_DEBUG_FS
	codec->component.init_debugfs = soc_init_codec_debugfs;
	codec->component.debugfs_prefix = "codec";
#endif

	if (codec_drv->get_regmap)
		codec->component.regmap = codec_drv->get_regmap(dev);

	for (i = 0; i < num_dai; i++) {
		fixup_codec_formats(&dai_drv[i].playback);
		fixup_codec_formats(&dai_drv[i].capture);
	}

	ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
	if (ret < 0) {
		dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret);
		goto err_cleanup;
	}

	list_for_each_entry(dai, &codec->component.dai_list, list)
		dai->codec = codec;

	mutex_lock(&client_mutex);
	snd_soc_component_add_unlocked(&codec->component);
	list_add(&codec->list, &codec_list);
	mutex_unlock(&client_mutex);

	dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n",
		codec->component.name);
	return 0;

err_cleanup:
	snd_soc_component_cleanup(&codec->component);
err_free:
	kfree(codec);
	return ret;
}

首先申请snd_soc_codec实例:
在这里插入图片描述

随后确定codec的名字,这个名字很重要,Machine驱动定义的snd_soc_dai_link中会指定每个link 的codec和 dai的名字,进行匹配绑定时就是通过和这里的名字比较,从而找到该Codec的!
在这里插入图片描述
此函数在snd_soc_component_initialize中调用的。

然后初始化它的各个字段,多数字段的值来自上面定义的snd_soc_codec_driver的实例。
在这里插入图片描述

随后通过snd_soc_register_dais函数对本Codec的dai进行注册:
在这里插入图片描述
把dai_driver添加到component->dai_list中去。

最后把codec驱动添加到codec_list中去,至此完成codec的注册。
![在这里插入图片描述](https://img-blog.csdnimg.cn/0de882f084c043bca108628818cc9a6d.png在这里插入图片描述

五、codec的初始化

Machine驱动的初始化(请参考alsa-machine驱动部分),codec和 dai的注册,都会调用snd_soc_instantiate_cards()进行一次声卡和codec,dai,platform的匹配绑定过程,这里所说的绑定,正如Machine驱动一文中所描述,就是通过3个全局链表,按名字进行匹配,把匹配的codec,dai和platform实例赋值给声卡每对dai的snd_soc_pcm_runtime变量中。一旦绑定成功,将会使得codec和dai驱动的probe回调被调用,codec的初始化工作就在该回调中完成。对于WM8960,该回调就是wm8960_probe函数:

wm8960_probe实例如下:
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

六、Audio-ALSA架构中的codec 的相关文章

  • 适用于 Linux 的轻量级 IDE [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 在 C 中使用单个消息队列是否可以实现双向通信

    我希望服务器向客户端发送一些消息 并让客户端确认它 我被分配了这个任务 我可以在 C linux 中使用单个消息队列来完成它还是我需要创建两个 谢谢 是的 可以使用 sysV 消息队列来做到这一点 从您之前的问题来看 您正在使用该队列 您可
  • Android MediaExtractor seek() 对 MP3 音频文件的准确性

    我在使用 Android 时无法在eek 上获得合理的准确度MediaExtractor 对于某些文件 例如this one http www archive org download emma solo librivox emma 01
  • 在centos中安装sqlite3 dev和其他包

    我正在尝试使用 cpanel 在 centos 机器上安装 sqlite dev 和其他库 以便能够编译应用程序 我对 debian 比 centos 更熟悉 我知道我需要的库是 libsqlite3 dev libkrb5 dev lib
  • 在 Linux 上以编程方式设置 DNS 名称服务器

    我希望能够通过我的 C C 程序为 Linux 上的 DNS 名称服务器添加 IP 地址 我在一个带有只读 etc resolv conf 的嵌入式平台上 这意味着我不能简单地将 nameserver xxx xxx xxx xxx 行添加
  • 如何在 Ubuntu 中创建公共 HTML 文件夹?

    简单的问题 但由于某种原因我无法在谷歌上找到确切的答案 我在 Slicehost 上安装了全新的 Ubuntu 并且想在我的主目录中为包含一堆静态 HTML 文件的简单网站创建一个公共目录 我该怎么做呢 只是打字的问题吗mkdir publ
  • 如何在linux中以编程方式获取dir的大小?

    我想通过 C 程序获取 linux 中特定目录的确切大小 我尝试使用 statfs path struct statfs 但它没有给出确切的大小 我也尝试过 stat 但它返回任何目录的大小为 4096 请建议我如何获取 dir 的确切大小
  • C 语言的符号表

    我目前正在开发一种执行模式匹配的静态分析工具 我在用Flex https github com westes flex生成词法分析器 我编写了代码来管理符号表 我不太有经验C 所以我决定将符号表实现为线性链表 include
  • 如何根据标签将单个 XML 文件拆分为多个

    我有一个带有标签的 XML 文件 我想像这样分割文件
  • 如何获取 (Linux) 机器的 IP 地址?

    这个问题和之前问的几乎一样如何获取本地计算机的IP地址 https stackoverflow com questions 122208 get the ip address of local computer 问题 但是我需要找到一个的I
  • 使用 MAX_ORDER / 包含 mmzone.h

    根据https www kernel org doc Documentation networking packet mmap txt https www kernel org doc Documentation networking pa
  • Bash - 在与当前终端分开的另一个终端中启动命令的新实例

    我有一个简单的 bash 脚本 test sh 设置如下 bin bash args if args 0 check capture then watch n 1 ls lag home user capture0 watch n 1 ls
  • 在 VB2010 Windows 窗体开始时播放 .wav/.mp3 文件?

    制作 VB2010 已经大约一年了 最近开始突破我可以将哪种媒体合并到我的表单中的界限 但我无法播放 wav 或 mp3 文件 我尝试按照微软和其他编码网站上的教程进行操作 但没有成功 任何帮助 将不胜感激 要播放波形文件 您可以简单地使用
  • Mac OS X 上的 /proc/self/cmdline / GetCommandLine 等效项是什么?

    如何在不使用 argc argv 的情况下访问 Mac OS X 上的命令行 在 Linux 上 我会简单地阅读 proc self cmdline or use GetCommandLine在 Windows 上 但我找不到 Mac OS
  • C修改printf()输出到文件

    有没有办法修改printf为了将字符串输出到文件而不是控制台 我尝试在互联网上查找一些内容 发现了类似的电话dup dup2 and fflush这可能与此有关 EDIT 也许我不清楚 问题是这是C考试问题 问题如下 解释一个通常将字符串输
  • 错误:“rjags”的包或命名空间加载失败

    在终端的 conda 环境之一中 我能够成功安装包 rjags 但是 当我在该环境中运行 R 并运行库 rjags 时 出现以下错误 加载所需的包 coda 错误 rjags 的包或命名空间加载失败 rjags 的 loadNamespac
  • 无限实时连续传输音频信号,Python

    我有一个简单的问题 在 Python 中从音频插孔流式传输音频信号时 使用 pyaudio 库如何继续流式传输音频信号 直到我选择 停止 程序 示例 我们的方式捕捉我们的网络摄像头 https docs opencv org 3 0 bet
  • 检查已安装的软件包,如果没有找到则安装

    我需要检查已安装的软件包 如果未安装则安装它们 RHEL CentOS Fedora 示例 rpm qa grep glibc static glibc static 2 12 1 80 el6 3 5 i686 如何在 BASH 中进行检
  • 复制目录内容

    我想将目录 tmp1 的内容复制到另一个目录 tmp2 tmp1 可能包含文件和其他目录 我想使用C C 复制tmp1的内容 包括模式 如果 tmp1 包含目录树 我想递归复制它们 最简单的解决方案是什么 我找到了一个解决方案来打开目录并读
  • x86-64 AMD 上 CALL 指令的操作数生成

    以下是示例程序 objdump 的输出 080483b4

随机推荐

  • windows下添加路由

    1 首先在 运行 窗口输入cmd 按WIN R打开运行窗口 然后回车进入命令行 2 在命令行下输入route命令 会有对应的提示信息 ROUTE f p 4 6 command destination MASK netmask gatewa
  • 【单目标优化算法】杂草优化算法(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 杂草算法代码简单 易于实现 具有较强的自适
  • C#根据经纬度计算两点距离(单位是千米)

    SQL CREATE function dbo fnGetDistance LatBegin REAL LngBegin REAL LatEnd REAL LngEnd REAL RETURNS FLOAT AS BEGIN DECLARE
  • java-面试题

    来源 牛客网 1 什么是Java虚拟机 为什么Java被称作是 平台无关的编程语言 Java虚拟机是一个可以执行Java字节码的虚拟机进程 Java源文件被编译成能被Java虚拟机执行的字节码文件 java的跨平台不是java源程序的跨平台
  • 小程序多种姿势更换文章

    概述 简单的文章切换demo 通过倒计时 摇一摇 双击进行文章切换 详细 直接看效果图吧 比较简单 主要是练习一下 小程序不带双击事件 可以记录第一次单击事件和第二次单机事件进行双击操作 1 摇一摇是通过调用官方的 2 wx onAccel
  • MATLAB安装MinGW-w64 C/C++ 编译器

    参考链接 matlab安装 MinGW w64 C C 编译器 上面的链接非常详细 可以之间看上面的 下面是我安装过程的总结 1 官网安装不成功 2 MinGW w64 for 32 and 64 bit Windows download
  • 通过Python在Windows或Linux上快速搭建HTTP服务器

    在Windows 7 10或Ubuntu上可以通过python2 x或python3 x来快速搭建一个简单的HTTP服务器 如果python为2 x 则可执行 python m SimpleHTTPServer 或 python2 m Si
  • 网上能搞什么副业?适合网上搞的副业

    现如今互联网时代 线上副业早已经普遍 有些工作不再需要线下交谈对接 只要有网络就可以搞定 线上副业已经不再是个人的选择 而是刚需 很多普通人的工资收入都不高 但是又不敢轻易辞职 于是就冒出了开启副业的想法 互联网的兴起让一大批人实现了副业收
  • Python的virtualenv虚拟环境

    Python的virtualenv虚拟环境 1 安装虚拟环境 1 1 创建虚拟环境 1 2 激活虚拟环境 1 3 退出虚拟环境 1 4 删除虚拟环境 2 使用虚拟环境 其实一直在Python的虚拟环境 确实很好用 这次想总结一下 好方便自己
  • 性能需求指标

    通常我们都从两个层面定义性能场景的需求指标 业务指标和技术指标 性能测试行业常用的性能指标表示法 TPS 每秒事务数 T没有规定 所有相关的人都要知道你的 T 是如何定义的 通常情况下 我们会根据场景的目的来定义 TPS 的粒度 如果是接口
  • python开发工程师是干嘛的-python工程师是做什么的

    对于大多数人来说 可能只知道python这个名字 或者也听说过Python在云计算 大数据 人工智能里面都有运用 学Python未来发展前景还比较好 但是并不知道Python具体可以做些什么 在哪些应用场景下可以使用Python语言 最近也
  • 品味树莓派:GPIO Zero库远程控制功能使用

    文章目录 目的 使用前准备 树莓派设置 控制端设置 远程控制GPIO口 总结 目的 使用GPIO Zero库可以通过网络远程控制树莓派的上GPIO口 本文将对该功能进行简单说明 使用前准备 树莓派设置 使用GPIO Zero库远程功能首先需
  • Unity实现2D游戏跟随摄像机(平滑移动)

    文章目录 运行效果 玩家角色 脚本 字段 跟随逻辑 完整代码 其他相关文章连接 运行效果 摄像机运行效果如下所示 玩家角色 首先创建一个可用的玩家角色 写好移动逻辑 如果要使用在Unity商店中购买的资源 可以点击Window菜单栏 gt
  • 【Hexo】【Blog】更换主题【简】

    下载 Hexo themes Github NEXT主题 新建文件夹 这里想偷个懒一次装好 需要安装一些插件工具 有些小头疼 还是从GitHub拉下来再去改个性化的界面吧 ps 用git拉下来的话也比较慢 如果不换源的话 我依旧down下来
  • 解密Kerberos流量

    以下主要演示如何通过将keytab导入到wireshark 实现对Kerberos协议进行解密 Keytab 那么Keytab是什么 keytab是可以理解为一个密钥表 是key table的缩写 用途类似于用户的密码 keytab中包含一
  • Docker实战:docker compose 搭建Rocketmq

    1 配置文件准备 1 1 新建目录 home docker data rocketmq conf mkdir home docker data rocketmq conf 1 2 在上面目录下新建文件broker conf文件 内容如下 b
  • JSX的基础使用(二)

    1 事件绑定 React 事件的 命名采用小驼峰式 camelCase 而不是纯小写 们需要 通过 传入一个事件处理函数 这个函数会在事件发生时被执行 2 this绑定 bind给btnClick显示绑定this 使用 ES6 class
  • 面向过程编程

    p 面向过程编程 p p p
  • Qt 使用中文引起的错误error: C2001: 常量中有换行符;error: C1071: 在注释中遇到意外的文件结束

    中文编码问题导致的错误 解决办法 1 使用英文注释 2 在中文的结尾加上英文 或者空格 3 修改设置 编辑 gt 选择编码 gt system gt 编码保存
  • 六、Audio-ALSA架构中的codec

    一 codec简介 处理器如果既想 听到 外界的声音 又想向外界传达自己的 心声 那么就需要同时用到 DAC 和 ADC 这两款芯片 那是不是买两颗 DAC 和 ADC 芯片就行了呢 答案肯定是可以的 但是音频不单单是能出声 能听到就行 我