【音视频|ALSA】基于alsa-lib开发ALSA应用层程序--附带源码
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭基于alsa-lib开发ALSA应用层程序🍭
😎金句分享😎:🍭盖士人读书,第一要有志,第二要有识,第三要有恒。有志则断不甘为下流,有识则知学问无尽,不敢以一得自足,如河伯之观海,如井蛙之窥天,皆无识者也。有恒则断无不成之事,此三者缺一不可。 ——《曾国藩家书》🍭
【音视频|ALSA】ALSA是什么?ALSA框架详细介绍
【音视频|ALSA】SS528开发板编译Linux内核ALSA驱动、移植alsa-lib、采集与播放usb耳机声音
目录
- 一、ALSA应用层开发基础知识
- 二、alsa-lib常用函数
- 三、编写ALSA应用层程序
- 3.1 alsa播放程序开发--alsa-playback.c
- 3.2 alsa录制音频程序开发--alsa-capture.c
- 四、XRUN( underrun和overrun)
- 五、总结
![]()
一、ALSA应用层开发基础知识
sample:样本,采样点。数字音频最小单位,其大小与位宽有关,一般为8bit(1个字节)、16bit(2个字节);channel:声道,一般单声道(mono)和立体声(stereo),还有一些多声道如5.1声道。frame:帧,一个完整的声音单元,即单次采样的所有声道的数据。frame=sample*channel;
例如:48Khz、16位的立体声PCM流的1帧是4个字节。sample rate:采样率,即每秒的采样次数。如果采样率为48kHz,则说明一秒采样48000帧。period size:周期大小,是每次硬件中断之间的帧数。buffer size:缓冲区大小,必须大于一个周期的大小。一般为周期大小的2倍。单位也是帧数。
例子:
结合上面的知识点,这里以48kHz、16bit的立体声音频流举例:
- 16bit则每个样本为2个字节,
- 立体声表示有2个声道,
- 48kHz 表示每秒有48000个音频帧。
由此可以计算出每秒钟传输的数据大小:
2 * 2 * 48000=192000字节;
现在如果ALSA每秒钟产生一个硬件中断,在每秒结束时,我们需要准备好192000字节;
如果它每半秒中断一次,对于同一个流,我们需要在每次中断时准备好192000/2 = 96000字节;
如果每100毫秒发生一次中断,我们需要在每次中断时准备好192000*(0.1/1) = 19200字节。
我们可以通过设置周期大小(以帧为单位)来控制PCM中断的产生时间。
如果我们将48kHz、16bit的立体声音频流的period size设置为4800帧(也就是480022=19200字节),则每19200字节就会产生一个中断,也就是100ms。
相应地,buffer size至少应为2*period_size = 2*4800= 9600帧(960022 = 38400字节)。
实际编程中,可能需要计算一个周期的总字节数
period bytes,就是等于period size乘以每一帧的大小。同样的,buffer的总字节数buffer bytes等于buffer size乘以一帧大小。
如果已知音频的采样率、通道数、位宽、周期数,则
buffer size、buffer time、period size、period time这四个值可以相互推断出来:
以48000Hz采样率、2声道、16bit、4周期来举例,这样的音频流一秒钟的帧数是48000帧,如果buffer size是48000,则buffer time刚好就是一秒;如果buffer time是500ms,则buffer size是24000帧。
而period size=buffer size/周期数;period time=buffer time/周期数
![]()
二、alsa-lib常用函数
alsa-lib的函数声明在pcm.h,总共可以分为16个模块:
- PCM Interface
- Stream Information
- Hardware Parameters
- Software Parameters
- Access Mask Functions
- Format Mask Functions
- Subformat Mask Functions
- Status Functions
- Description Functions
- Debug Functions
- Direct Access (MMAP) Functions
- Helper Functions
- Hook Extension
- Scope Plugin Extension
- Simple setup functions
- Deprecated Functions
可以在官方文档查看对应的模块函数说明:
https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html
下表介绍一些常用的函数:
| 函数 | 说明 |
|---|---|
| snd_pcm_open | 根据pcm设备名字打开一个pcm设备 |
| snd_pcm_hw_params_malloc | 使用标准malloc分配无效的snd_pcm_hw_params_t |
| snd_pcm_hw_params_any | 用PCM的完整配置空间填充参数。 |
| snd_pcm_hw_params_set_access | 将配置空间限制为仅包含一种访问类型。 |
| snd_pcm_hw_params_set_format | 将配置空间限制为仅包含一种格式。 |
| snd_pcm_hw_params_set_channels | 将配置空间限制为仅包含一个通道计数。 |
| snd_pcm_hw_params_set_rate_near | 将配置空间限制为具有最接近目标的速率。 |
| snd_pcm_hw_params_get_buffer_time_max | 从配置空间中提取最大缓冲时间。 |
| snd_pcm_stream | 获取PCM句柄的流 |
| snd_pcm_hw_params_set_buffer_time_near | 限制配置空间以使缓冲时间最接近目标。 |
| snd_pcm_hw_params_set_period_time_near | 限制配置空间以使周期时间最接近目标。 |
| snd_pcm_hw_params | 安装从配置空间中选择的一个PCM硬件配置,并调用snd_pcm_prepare。 |
| snd_pcm_nonblock | 设置非阻塞模式 |
| snd_pcm_hw_params_get_period_size | 从配置空间中提取周期大小。 |
| snd_pcm_hw_params_get_buffer_size | 从配置空间中提取周期大小。 |
| snd_pcm_format_physical_width | 返回存储PCM样本所需的位。 |
更多函数说明参考:
https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___h_w___params.html
https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html
![]()
三、编写ALSA应用层程序
这小节介绍简单的ALSA应用程序开发流程,以及给出例子源码。
在编写代码前,可以先使用cat /proc/asound/card0/stream0查看ALSA设备支持的参数:
# cat /proc/asound/card0/stream0
Rapoo Gaming Headset at usb-10300000.xhci_0-1.1, full speed : USB AudioPlayback:Status: StopInterface 1Altset 1Format: S16_LEChannels: 2Endpoint: 1 OUT (ADAPTIVE)Rates: 48000, 44100Capture:Status: StopInterface 2Altset 1Format: S16_LEChannels: 1Endpoint: 2 IN (ASYNC)Rates: 48000, 44100
Playback:播放设备
Capture:录音设备
Interface:接口序号
Format:格式
Channels:通道数
Rates:支持的采样率
3.1 alsa播放程序开发–alsa-playback.c
开发流程:
- 1、打开设备:调用
snd_pcm_open,指定类型为SND_PCM_STREAM_PLAYBACK,以及设备名称,打开设备;- 2、设置硬件参数
设置存取方式、格式、通道数、采样率、缓冲时间、周期时间,最后将参数写入设备;
如果有一些参数不清楚怎么设置,可以使用命令cat /proc/asound/card0/stream0查看支持的参数:- 3、播放音频
每次往alsa驱动写入一个周期大小的字节,不足一周期的要填0;- 4、释放资源,关闭设备
下面是一个非常简单的ALSA播放音频的代码,复制后保存为alsa-playback.c,使用命令aarch64-mix210-linux-gcc alsa-playback.c -I /usr/lib/alsa-lib-1.2.10/include/ -L /usr/lib/alsa-lib-1.2.10/lib/ -l asound -lpthread -ldl -lm -o alsa-playback 已编译通过。
48000Hz-16bit-2ch-ChengDu.pcm 文件下载:https://download.csdn.net/download/wkd_007/88421282
// alsa-playback.c
// aarch64-mix210-linux-gcc alsa-playback.c -I /usr/lib/alsa-lib-1.2.10/include/ -L /usr/lib/alsa-lib-1.2.10/lib/ -l asound -lpthread -ldl -lm -o alsa-playback/*
* snd_pcm_hw_params_alloca 申请的内存在函数返回后会自动释放,不需要手动释放。这个函数会在栈上分配一块内存,函数返回后,栈上的内存会自动被回收。
*/
#include <stdio.h>
#include <alsa/asoundlib.h>#define PCM_NAME "hw:0,0"
#define PLAYBACK_FILE "48000Hz-16bit-2ch-ChengDu.pcm"snd_pcm_hw_params_t *hw_params;
static unsigned int rate = 48000; /* stream rate */int set_hw_params(snd_pcm_t *handle, int format, int channels, snd_pcm_uframes_t *period_frames)
{int err = -1;// 分配硬件参数空间,调用 alloca 在栈分配内存,函数结束后自动释放,不需要调用 snd_pcm_hw_params_freesnd_pcm_hw_params_alloca(&hw_params);//1、以默认值填充硬件参数if ((err = snd_pcm_hw_params_any(handle, hw_params)) < 0) {return err;}//2、 Restrict a configuration space to contain only real hardware rates.if ((err = snd_pcm_hw_params_set_rate_resample(handle, hw_params, 0)) < 0) {return err;}//3、设置存取方式为交叉存储if ((err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {return err;}//4、设置格式,S16_LE等if ((err = snd_pcm_hw_params_set_format(handle, hw_params, format)) < 0) {return err;}//5、设置通道if ((err = snd_pcm_hw_params_set_channels(handle, hw_params, channels)) < 0) {return err;}//6、大致设置采样率unsigned int rrate;rrate =rate;if ((err = snd_pcm_hw_params_set_rate_near(handle, hw_params, &rrate, NULL)) < 0) {return err;}//7、设置缓冲时间unsigned int buffer_time, period_time;// 先获取缓存时间if((err = snd_pcm_hw_params_get_buffer_time_max(hw_params, &buffer_time, 0))<0){return err;}if (buffer_time > 500000){buffer_time = 500000; // 500ms读取完整个buffer,结合下面代码一个周期就是 buffer_time/4=125ms,每个周期会产生一个中断printf("[%s %d] buffer_time=%d, irq=%d\n",__FILE__,__LINE__,buffer_time, buffer_time/4);}// 设置缓冲时间if ((err = snd_pcm_hw_params_set_buffer_time_near(handle, hw_params, &buffer_time, 0)) < 0) {return err;}// 8、设置周期时间,也就是中断时间period_time = buffer_time / 4;if ((err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &period_time, 0)) < 0) {return err;}// 9、将参数写入设备if ((err = snd_pcm_hw_params(handle, hw_params)) < 0){return err;}snd_pcm_uframes_t buffer_frames;snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_frames);if(period_frames != NULL) {//获取一个周期有多少帧数据if((err =snd_pcm_hw_params_get_period_size(hw_params, period_frames, 0)) < 0){printf("cannot get period size (%s)\n", snd_strerror(err));return err;}}if(err = (snd_pcm_nonblock(handle, 1) < 0)){return err;}// 10、释放 snd_pcm_hw_params_malloc 分配的内存//snd_pcm_hw_params_free(hw_params);return 0;
}int main()
{int err = -1;snd_pcm_t *playback_handle;snd_pcm_uframes_t period_frames; // 一周期的帧数// 1、打开设备if((err = snd_pcm_open(&playback_handle, PCM_NAME, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {printf("cannot snd_pcm_open (%s)\n",snd_strerror(err));return -1;}// 2、设置硬件参数set_hw_params(playback_handle, SND_PCM_FORMAT_S16_LE, 2, &period_frames);// 3、播放音频// 3.1 打开pcm文件int fd = open(PLAYBACK_FILE,O_RDONLY,0644);if(fd < 0){printf("open %s error!!!\n",PLAYBACK_FILE);return -1;}// 3.2 获取一周期的字节数const int period_bytes = snd_pcm_frames_to_bytes(playback_handle,period_frames);char *playback_buf = malloc(period_bytes);// 3.3 循环播放音频int readframes = 0;while(readframes = read(fd, playback_buf, period_bytes)) {//解决最后一个周期数据问题if(readframes < period_bytes) {memset(playback_buf+readframes, 0, period_bytes-readframes);}//向PCM写入数据,播放err = snd_pcm_writei(playback_handle, playback_buf, period_frames);if(err == -EPIPE) {snd_pcm_prepare(playback_handle);fprintf(stderr, "<<< snd_pcm_writei --> Buffer Underrun >>> \n");err = snd_pcm_writei(playback_handle, playback_buf, period_frames);if(err != period_frames) {printf("write to audio interface failede err:%d (period_frames:%d)\n",err,period_frames);break;}}else if(err != period_frames) {printf("write to audio interface failede err:%d (period_frames:%d)\n",err,period_frames);break;}//printf("process:playback wrote %d frames\n",period_frames);//usleep(100*1000);usleep(130*1000); //测试用,超过 125ms,会报错 Underrun}// 4.释放资源,关闭设备free(playback_buf);close(fd);snd_pcm_close(playback_handle);return 0;
}
3.2 alsa录制音频程序开发–alsa-capture.c
开发流程:
- 1、打开设备:调用
snd_pcm_open,指定类型为SND_PCM_STREAM_CAPTURE,以及设备名称,打开设备;- 2、设置硬件参数
设置存取方式、格式、通道数、采样率、缓冲时间、周期时间,最后将参数写入设备;
如果有一些参数不清楚怎么设置,可以使用命令cat /proc/asound/card0/stream0查看支持的参数:- 3、读取音频
每次从alsa驱动读取一个周期大小的字节;- 4、释放资源,关闭设备
// alsa-capture.c
// aarch64-mix210-linux-gcc alsa-capture.c -I /usr/lib/alsa-lib-1.2.10/include/ -L /usr/lib/alsa-lib-1.2.10/lib/ -l asound -lpthread -ldl -lm -o alsa-capture/*
* snd_pcm_hw_params_alloca 申请的内存在函数返回后会自动释放,不需要手动释放。这个函数会在栈上分配一块内存,函数返回后,栈上的内存会自动被回收。
*/
#include <stdio.h>
#include <alsa/asoundlib.h>#define PCM_NAME "hw:0,0"
#define CAPTURE_FILE "alsa-capture.pcm"snd_pcm_hw_params_t *hw_params;
static unsigned int rate = 48000; /* stream rate */int set_hw_params(snd_pcm_t *handle, int format, int channels, snd_pcm_uframes_t *period_frames)
{int err = -1;// 分配硬件参数空间,调用 alloca 在栈分配内存,函数结束后自动释放,不需要调用 snd_pcm_hw_params_freesnd_pcm_hw_params_alloca(&hw_params);//1、以默认值填充硬件参数if ((err = snd_pcm_hw_params_any(handle, hw_params)) < 0) {return err;}//2、 Restrict a configuration space to contain only real hardware rates.if ((err = snd_pcm_hw_params_set_rate_resample(handle, hw_params, 0)) < 0) {return err;}//3、设置存取方式为交叉存储if ((err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {return err;}//4、设置格式,S16_LE等if ((err = snd_pcm_hw_params_set_format(handle, hw_params, format)) < 0) {return err;}//5、设置通道if ((err = snd_pcm_hw_params_set_channels(handle, hw_params, channels)) < 0) {return err;}//6、大致设置采样率unsigned int rrate;rrate =rate;if ((err = snd_pcm_hw_params_set_rate_near(handle, hw_params, &rrate, NULL)) < 0) {return err;}//7、设置缓冲时间unsigned int buffer_time, period_time;// 先获取缓存时间if((err = snd_pcm_hw_params_get_buffer_time_max(hw_params, &buffer_time, 0))<0){return err;}if (buffer_time > 500000){buffer_time = 500000; // 500ms写完整个buffer,结合下面代码一个周期就是 buffer_time/4=125ms,每个周期会产生一个中断printf("[%s %d] buffer_time=%d, irq=%d\n",__FILE__,__LINE__,buffer_time, buffer_time/4);}// 设置缓冲时间if ((err = snd_pcm_hw_params_set_buffer_time_near(handle, hw_params, &buffer_time, 0)) < 0) {return err;}// 8、设置周期时间,也就是中断时间period_time = buffer_time / 4;if ((err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &period_time, 0)) < 0) {return err;}// 9、将参数写入设备if ((err = snd_pcm_hw_params(handle, hw_params)) < 0){return err;}snd_pcm_uframes_t buffer_frames;snd_pcm_hw_params_get_buffer_size(hw_params, &buffer_frames);if(period_frames != NULL) {//获取一个周期有多少帧数据if((err =snd_pcm_hw_params_get_period_size(hw_params, period_frames, 0)) < 0){printf("cannot get period size (%s)\n", snd_strerror(err));return err;}}if(err = (snd_pcm_nonblock(handle, 1) < 0)){return err;}// 10、释放 snd_pcm_hw_params_malloc 分配的内存//snd_pcm_hw_params_free(hw_params);return 0;
}int main()
{int err = -1;snd_pcm_t *capture_handle;snd_pcm_uframes_t period_frames; // 一周期的帧数// 1、打开设备if((err = snd_pcm_open(&capture_handle, PCM_NAME, SND_PCM_STREAM_CAPTURE, 0)) < 0) {printf("cannot snd_pcm_open (%s)\n",snd_strerror(err));return -1;}// 2、设置硬件参数set_hw_params(capture_handle, SND_PCM_FORMAT_S16_LE, 1, &period_frames);// 3、获取音频// 3.1 打开录制文件int fd = open(CAPTURE_FILE,O_RDWR | O_TRUNC | O_CREAT,0644);if(fd < 0){printf("open %s error!!!\n",CAPTURE_FILE);return -1;}// 3.2 获取一周期的字节数const int period_bytes = snd_pcm_frames_to_bytes(capture_handle,period_frames);char *capture_buf = malloc(period_bytes);int count = 100; // 捕获100个周期int readframes = 0;while(count--) {//向PCM读一周期数据memset(capture_buf,0,period_bytes);if((readframes = snd_pcm_readi(capture_handle, capture_buf, period_frames)) < 0) {if(readframes == -EPIPE)printf("read from audio interface failed (%d), overrun, Need to read faster\n",readframes);elseprintf("read from audio interface failed (%d)\n",readframes);break;}printf("--process:capture read %d frames\n",readframes);write(fd,capture_buf,snd_pcm_frames_to_bytes(capture_handle,readframes));usleep(100*1000); //usleep(130*1000); //测试用,超过 125ms,会报错 overrun}// 4、释放资源,关闭设备free(capture_buf);close(fd);snd_pcm_close(capture_handle);return 0;
}
![]()
四、XRUN( underrun和overrun)
在 ALSA 数据传输中,最容易出现的错误是 underrun 和 overrun。
underrun:pcm 播放的时候,接口 snd_pcm_writei 返回 -EPIPE,为 underrun(不足)
出现这问题原因是应用准备的音频数据不够,比如,驱动需要播放需要 1026 帧数据,但应用只准备好了 1024 帧。可以根据采样率和buffer size、period size去调整;overrun:录制音频的时候, 接口 snd_pcm_readi 返回 -EPIPE, 为 overrun(超载)
alsa驱动一直往buffer里面写,但应用程序却读取的很慢,例如:驱动写了1026帧,而应用层只读取了1024帧。需要加快读取速度。或者调整buffer size、period size。
![]()
五、总结
文章介绍了alsa的基础知识,以及基于alsa-lib开发ALSA应用层程序的开发流程和alsa开发过程钟常见的报错,提供了简单的alsa应用层代码。
参考资料:
ALSA官网资料——FramesPeriods:https://alsa-project.org/main/index.php/FramesPeriods
【Linux&音频】Alsa音频编程【精华】:https://blog.csdn.net/u012183924/article/details/53407668
ALSA 音频数据传输 underrun 和 overrun:https://blog.csdn.net/qq_38350702/article/details/111995039
Linux应用开发【第八章】ALSA应用开发:https://blog.csdn.net/thisway_diy/article/details/121809633

如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁
相关文章:
【音视频|ALSA】基于alsa-lib开发ALSA应用层程序--附带源码
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…...
嵌入式养成计划-43----QT QMainWindow中常用类的使用--ui界面文件--资源文件的添加--信号与槽
一百零九、QMainWindow中常用类的使用 109.1 菜单栏 QMenuBar 菜单栏 QMenuBar 最多只能有一个 109.2 工具栏 QToolBar 工具栏 QToolBar 可以有多个 109.3 状态栏QStatusBar 状态栏 QStatusBar 最多只能有一个 109.4 浮动窗口QDockWidget 浮动窗口 可以有多个 109.5 代…...
【Yarn】清除Yarn的缓存,更新Yarn本身、更新项目的依赖项
要清除Yarn的缓存,可以运行以下命令: yarn cache clean这将清除Yarn的缓存目录。 要更新Yarn本身,可以运行以下命令: yarn self-update这将下载并安装最新版本的Yarn。 如果要更新项目的依赖项,可以运行以下命令&a…...
点云从入门到精通技术详解100篇-雨雾环境下多传感器融合SLAM方法(续)
目录 4 基于球面投影的激光视觉融合里程计 4.1 引言 4.2 视觉惯性里程计 4.2.1特征点提取与匹配...
解决GET请求入参@NotNull验证不生效问题
一、问题 get请求NotNull验证不生效 二、解决方案 两个步骤: 在该方法的controller类上加Validated;在参数面前加NotNull; 三、其他注解 //被注释的元素必须为null Null //被注释的元素不能为null NotNull //被注释的元素必须为true Ass…...
《golang设计模式》第三部分·行为型模式-01-责任链模式(Chain of Responsibility)
文章目录 1 概念1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1 概念 责任链(Chain of Responsibility)是指将客户端请求处理的不同职责对象组成请求处理链。 客户端只需要将请求交付到该链上,而不需要关心链上含有哪些对象。请求…...
环境变量【使用命令行参数引出环境变量】
前提:命令行参数 大家在写C/C程序的时候肯定见过下面这种情况: main函数里面携带的参数,平常写代码过程中很少用到这两个参数,接下来我们就研究一下 我们也不知道 指针数组argv里面到底保存的是什么,也不知道这个a…...
【Java 进阶篇】JavaScript BOM History 详解
当用户浏览网页时,可以使用JavaScript的BOM (Browser Object Model)中的History对象来访问浏览器的历史记录。这个对象允许您在不更改页面的情况下导航到不同的历史记录项,或者查看有关用户访问过的页面的信息。 在本篇博客中,我们将围绕Jav…...
【计算机网络】https协议
文章目录 1 :peach:基本概念:peach:1.1 :apple:什么是HTTPS?:apple:1.2 :apple:什么是加密?:apple:1.3 :apple:常见的加密方式:apple:1.3.1 :lemon:对称加密:lemon:1.3.2 :lemon:⾮对称加密:lemon: 1.4 :lemon:数据指纹:lemon: 2 :peach:HTTPS的⼯作过程…...
React之受控组件和非受控组件以及高阶组件
一、受控组件 受控组件,简单来讲,就是受我们控制的组件,组件的状态全程响应外部数据 举个简单的例子: class TestComponent extends React.Component {constructor (props) {super(props);this.state { username: lindaidai }…...
中国移动集采120万部,助推国产5G赶超iPhone15
近期媒体纷纷传出消息指中国移动将大规模集采,预计将采购国产5G手机120万台,加上另外两家运营商的集采数量,估计集采数量可能达到300万部,如此将有助于它在国内高端手机市场赶超苹果。 国产5G手机在8月底突然上市,获益…...
华为云HECS服务器下docker可视化(portainer)
一、docker安装 华为云HECS安装docker-CSDN博客 二、portainer安装 portainer地址:Portainer: Docker and Kubernetes Management Platform 当前portainer分CE(开源版) 和 BE(商业版),用CE即可 1 创建…...
postman发送soap报文示例
一、soap简介 soap是一种基于XML的协议 二、postman发送soap请求 1、发送post请求,url: https://www.dataaccess.com/webservicesserver/NumberConversion.wso 2、headers设置,添加Content-Type,值为text/xml 添加SOAP…...
力扣-python-两数之和
题解: class Solution(object):def twoSum(self, nums, target):# 遍历列表for i in range(len(nums)):# 计算需要找到的下一个目标数字res target-nums[i]# 遍历剩下的元素,查找是否存在该数字if res in nums[i1:]:# 若存在,返回答案。这里…...
算水质TDS加温度补偿
先上图,就图里这款水质检测,用树莓派3/4的话,要配个温度检测作为温度校正,以及一个adc 元器件。我选ds18b20和ads1115。 再把模拟数据计算过程放一下: 温度检测元器件在农历钟那里提过,就是同款。此处先测个…...
wps/word 如何让表格的标题和表格名称文本(表1-1 xxx)跨页显示(已解决)
第一步: 打开wps 创建一个跨页的表格表格,如下图 第二步 大家都知道 表格标题跨页 就是1)在菜单表格工具 点击重复标题 或者 2)表格属性--》行--》在各页顶端以标题行形式出现,详细如下图。 1) 第一…...
攻防世界web篇-PHP2
直接点击进入到http网页中,会得到这样一个界面 这里,我最开始使用了burp什么包也没有抓到,然后接着又用nikto进行探测,得到的只有两个目录,当时两个目录打开后,一个是fond界面,一个是这个网页的…...
Kotlin中的步长
步长是 Kotlin 中用于迭代区间或集合时控制迭代步进的概念。在 Kotlin 中,我们可以使用 step 关键字来指定迭代时的步长。 在 Kotlin 中,有多种方式可以定义一个区间(Range)。我们将通过以下示例代码来展示不同类型的区间以及如何…...
3. 无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: s "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。示例 2: 输入: s "bbbbb" 输出: 1 解释: 因为无…...
通过SPI传输BMI160数据到nrf528xx
目录 主控和外设之间的联系关键示例可能的bug 主控和外设之间的联系 在完成代码之前,我们手里会有两份代码,一份是nrf528xx的SDK,一份是BMI160传感器的SDK,怎么利用SDK完成我们的需求呢?首先我们要搞明白,…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...


