【音视频|ALSA】基于alsa-lib开发ALSA应用层程序--附带源码

news2024/11/19 15:17:21

😁博客主页😁:🚀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 sizebuffer timeperiod sizeperiod 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 Audio

Playback:
  Status: Stop
  Interface 1
    Altset 1
    Format: S16_LE
    Channels: 2
    Endpoint: 1 OUT (ADAPTIVE)
    Rates: 48000, 44100

Capture:
  Status: Stop
  Interface 2
    Altset 1
    Format: S16_LE
    Channels: 1
    Endpoint: 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_free
    snd_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_free
    snd_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);
			else
				printf("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 sizeperiod size去调整;
  • overrun:录制音频的时候, 接口 snd_pcm_readi 返回 -EPIPE, 为 overrun(超载)
    alsa驱动一直往buffer里面写,但应用程序却读取的很慢,例如:驱动写了1026帧,而应用层只读取了1024帧。需要加快读取速度。或者调整buffer sizeperiod 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

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1105620.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【前端学习】—let const var之间的区别(十三)

【前端学习】—let const var之间的区别&#xff08;十三&#xff09; 一、let const var之间的区别 二、代码演示 <script>// 1、const let不存在变量提升 var 存在变量提升//var console.log(fullName,fullName);//fullName undefined //var fullNamecai; /* var 声明…

YOLO目标检测——肺结节数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;肺结节检测数据集主要应用于医学影像分析领域&#xff0c;特别是在肺结节检测和恶性风险评估方面。数据集说明&#xff1a;YOLO目标检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富。使用lableimg标注软件标注&#xff0c;标…

浅谈智能操控及无线测温产品在马来西亚理文造纸雪邦热电站项目中的应用

摘要&#xff1a;为避免因电线接头松动、电缆老化等引发高温造成火灾&#xff0c;所造成的的经济损失巨大&#xff0c;在一些电气设备如大电流电缆设备节点、电力设备局部异常升温部位、开关柜的动静触头&#xff0c;进线与出线母排接点、整流柜中的电气接点、变压器节点、变电…

win11 电脑 使用 python 连接USB 海康工业相机 运行示例程序 BasicDemo.py

win11 电脑 使用 python 连接USB 海康工业相机 运行示例程序 BasicDemo.py 1.下载海康的mvs客户端和mvs_sdk驱动 官网链接&#xff1a; https://www.hikrobotics.com/cn/machinevision/service/download?module0 下载这两个东西&#xff0c;然后安装 1.1 我的mvs安装在 F:\…

PHPstorm设置自定义代码自动提示,实现HTML页面的模版标签的提示

for {foreach $VAR0$ as $key>$vo } {/foreach}if {if $VAR0$$b }{else /}{/if}ifel {if $VAR0$$b}{elseif $a$b /}{else /}{/if}

不规则的文字环绕

不规则的文字环绕 我们将展示如何使用 CSS 的 shape-outside 和 clip-path 属性来实现不规则的文字环绕效果。 HTML 结构 首先&#xff0c;我们需要一个容器&#xff0c;其中包含一张图片和一段文字&#xff1a; <div class"container"><img src"i…

【算法tips】面试官:说说常见的排序算法。—— 巧记十种排序算法名称

做人呢最重要就是开心 面试时要放松、自信、从容&#xff0c; capybara教你巧记十种排序算法名称&#xff0c; 串联记忆&#xff0c;牢记不忘&#xff0c;快速忆起&#xff0c;从容回答。 十大经典排序算法 1.冒泡排序&#xff08;Bubble Sort&#xff09; 2.选择排序&…

Vue 模板字符串碰到script无法识别,报错Parsing error: Unterminated template.

需求&#xff1a; 将js代码完整的显示在界面上&#xff0c;包括标签 代码如下&#xff1a; 报错信息如下&#xff1a; 我们在上图中可以看到模板字符串加入了script标签后会报错 原因&#xff1a;运行JS的时候由上至下&#xff0c;先识别模板字符串里面的script标签&#xf…

svn 版本回退 并更新到最新

1.目前情况 (1)个人开发使用svn管理版本,没有分支只有主干. (2)最新版是89,想要完全回退到86,忽略所有86-89之间的变化 (3)直接回退后,提交的时候会提示更新(更新后又更新到最新版本了) (4)网上的教程测试都有问题(可能遇到的需求不一样) (5)工具为windows svn小乌龟 2.回…

Vue基础语法核心指令过滤器计算属性监听属性

目录 1. 模板语法 1.1 插值 1.1.1 文本 1.1.2 html 1.1.3 属性 1.1.4 表达式 1.2 指令 1.2.1 核心指令 1.2.1.1 v-if |v-else-if|v-else 1.2.1.2 v-show 1.2.1.3 v-for 1.2.1.4 v-on|v-model|v-for 1.2.1.5 参数 v-bind:href,v-on:click 1.2.1.6 简写 2. 过滤器…

优思学院|ISO9001管理体系从何而来?97%的专家不知道

ISO9001是当前全球最受欢迎的质量管理系统&#xff0c;特别是踏入21世纪以后&#xff0c;采用这套系统的政府部门、公营机构和企业数量都可谓几何级数上升。 截至2020年&#xff0c;ISO组织已向全球170个国家发出合共916842张ISO9001证书。 探源ISO9001的根源 那么&#xff…

代码随想录第45天 | ● 392.判断子序列 ● 115.不同的子序列

392.判断子序列 let ns.lengthlet a0if(n0)return true for(let i0;i<t.length;i){if(s[a]t[i])aif(an)return true}return false// s、t的长度const [m, n] [s.length, t.length];// dp全初始化为0const dp new Array(m 1).fill(0).map(x > new Array(n 1).fill(0))…

SpringBoot SSMP项目搭建保姆级教程

一、SpringBoot项目创建 Idea中创建New Project&#xff0c;选择Spring Initializr&#xff0c;输入Name、Location、JDK等&#xff0c;下一步。 选择合适的 SpringBoot 版本&#xff0c;点击创建。 此时&#xff0c;SpringBoot基础项目已经创建完毕。 二、Entity 开发 引入…

【AIFEM案例教程】压力容器热固耦合分析

AIFEM是由天洑自主研发的一款通用的智能结构仿真软件&#xff0c;助力用户解决固体结构相关的静力学、动力学、振动、热力学等实际工程问题&#xff0c;软件提供高效的前后处理工具和高精度的有限元求解器&#xff0c;帮助用户快速、深入地评估结构的力学性能&#xff0c;加速产…

天锐绿盾公司终端文件数据、资料防泄密系统——自动智能透明加密保护核心数据防止外泄软件

天锐绿盾公司终端文件数据防泄密系统采用高强度加密算法&#xff0c;对各种类型的文件进行实时、强制、透明的加解密。 该系统具有以下特点&#xff1a; 稳定性高&#xff1a;天锐绿盾文档透明加密安全系统是酷卫士电子文档安全管理平台的一个重要功能模块&#xff0c;性能稳定…

如何快速批量修改多个视频的MD5值?

在日常工作和生活中&#xff0c;我们经常需要处理大量的视频文件。有时候&#xff0c;我们需要修改这些视频文件的MD5值来满足某些需求。手动修改每个视频的MD5值是一项繁琐且容易出错的任务。幸运的是&#xff0c;有一种方法可以快速批量修改多个视频的MD5值&#xff0c;以下是…

[MySQL]BLOB/TEXT column ‘xxx‘ used in key specification without a key length

报错信息&#xff1a; SQLSTATE[42000]: Syntax error or access violation: 1170 BLOB/TEXT column xxx used in key specification without a key length 原因&#xff1a; MySQL的唯一索引不支持text类型的字段&#xff01;

Vue3开始

1. Vue3简介 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;海贼王&#xff09; 经历了&#xff1a;4800次提交、40个RFC、600次PR、300贡献者 官方发版地址&#xff1a;Release v3.0.0 One Piece vuejs/core 截止2023年…

网站防护的多种方案,如何让网站坚不可破

越权问题 问题叙述&#xff1a;存在权限管理不当的问题&#xff0c;导致用户可以越权访问资源。 改动提议&#xff1a;强化用户权限认证机制。 留意&#xff1a;通常&#xff0c;这种问题涉及到不同权限用户之间的资源访问&#xff0c;如浏览历史、cookie&#xff0c;以及标识…

软考公告 | 2023下半年软考模拟练习平台上线

软考办官方2023年下半年计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试模拟练习平台已开通&#xff0c;各位考生可于2023年10月16日-11月3日登录免费模拟练习。 模拟地址&#xff1a; https://bm.ruankao.org.cn/sign/welcome 01 软考模拟系统 大家可以登录…