虚拟声卡

2023-05-16

一、 虚拟声卡是什么?

虚拟声卡是一种软件产品,它只对声音数字信号进行处理。虽然它不能产生声音,但可以用来实现声音的传输、存储或混音等功能。

1) 虚拟声卡工作原理:

虚拟声卡通过软件技术实现了软声卡及声卡的驱动程序

虚拟声卡软件在计算机中为每一个虚拟声卡开辟一块内存,

应用程序可以通过虚拟声卡的音频输出端将音频流存储到开辟的内存中,同时可以通过虚拟声卡的输入端读取内存中的音频流。

每一个虚拟声卡的音频输入输出端的内部都是直连的,声音源程序产生(播放)声音,将声音送到虚拟声卡的音频输出端,虚拟声卡驱动程序直接将声音传到虚拟声卡的音频输入端。

应用程序 <---------->虚拟声卡(输出端 ----->一块内存 ----->虚拟声卡驱动程序   ----->音频输入 )

2) 虚拟声卡中声音的编码方式 pcm

3)根据实现方式大致可将虚拟设备分为两类

一类是在原有特定物理硬件设备的基础上采用软件的方式对这些设备加以抽象,如虚拟内存、虚拟存储器、虚拟网卡;

另一类则仅以纯软件编程的方式实现虚拟设备,这类虚拟设备主要有:虚拟光驱、虚拟声卡等。

虚拟设备 从单一功能设备-》多个功能设备-》通过网络完成将多个设备虚拟化发展。

虚拟设备技术 从逻辑角度而不是物理角度来对资源进行配置。

 

二、虚拟声卡实现技术

 通过对虚拟声卡源代码的分析,根据虚拟设备编程技术奖虚拟声卡的实现划为以下三个模块:

1)内核模式驱动:内核模式驱动程序是虚拟声卡技术的核心部分,执行大部分的响应用户需求的任务,内核模式驱动处理请求并填充好的数据包返回给用户模式驱动。(是被用户模式驱动的客户进程服务可以构造很多设备对象,并与他们虚连接)

2)用户模式驱动:用户模式驱动程序准备请求数据包并将数据包传给处理主要任务的内核模式驱动(用户模式驱动程序通过维持一个用户列表,驱动程序支持多用户操作。还具有内核模式驱动的服务管理功能,虚拟混频器)

3)控制面板程序:应用函数与用户模式驱动通信。 可以从内核驱动获得数据和修改内核驱动状态。

 

三、基于虚拟声卡(只处理声音数字信号)的各种异构(通信协议、语音编码)VOIP网络电话互通设计(通过开放API设置客户端声卡音频的输入和输出,获取PCM格式音频流)及实现 (解决VOIP通信技术在各个通信软件之间不能互通的问题的一个思路) 产品和业务互通 自动答录机和电话转接等功能(可以将一种网络电话输出的声音信息直接传给另一种网络电话的输入端)。

 

四、linux 虚拟声卡 dummy 创建

应用工程师需要用到一张虚拟声卡,以前没有接触过,这里mark一下。

内核配置如下:

  │     -> Device Drivers                                                                     
  │       -> Sound card support (SOUND [=y])                                                  
  │         -> Advanced Linux Sound Architecture (SND [=y])                                     
  │           -> Generic sound devices (SND_DRIVERS [=y])   

# aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Dummy [Dummy], device 0: Dummy PCM [Dummy PCM]
  Subdevices: 8/8
  Subdevice #0: subdevice #0
  Subdevice #1: subdevice #1
  Subdevice #2: subdevice #2
  Subdevice #3: subdevice #3
  Subdevice #4: subdevice #4
  Subdevice #5: subdevice #5
  Subdevice #6: subdevice #6
  Subdevice #7: subdevice #7
card 1: imxspdif [imx-spdif], device 0: S/PDIF PCM snd-soc-dummy-dai-0 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0
card 2: imxhdmisoc [imx-hdmi-soc], device 0: i.MX HDMI Audio Tx hdmi-hifi-0 []
  Subdevices: 1/1
  Subdevice #0: subdevice #0

 

 

DTS配置

dummy codec 驱动

RK系列SDK -- dummy codec虚拟声卡注册  https://blog.csdn.net/hb9312z/article/details/103315401

[RK3399][Android7.1] 调试笔记 --- 虚拟声卡驱动添加 https://blog.csdn.net/kris_fei/article/details/85237331

 

五、声卡和 PCM 设备的建立过程

ALSA表示高级Linux声音体系结构(Advanced Linux Sound Architecture)。它由一系列内核驱动,应用程序编译接口(API)以及支持Linux下声音的实用程序组成。这篇文章里,我将简单介绍 ALSA项目的基本框架以及它的软件组成。主要集中介绍PCM接口编程,包括您可以自动实践的程序示例。

ALSA体系结构

ALSA API可以分解成以下几个主要的接口: 
1 控制接口:提供管理声卡注册和请求可用设备的通用功能 
2 PCM接口:管理数字音频回放(playback)和录音(capture)的接口。本文后续总结重点放在这个接口上,因为它是开发数字音频程序最常用到的接口。 
3 Raw MIDI接口:支持MIDI(Musical Instrument Digital Interface),标准的电子乐器。这些API提供对声卡上MIDI总线的访问。这个原始接口基于MIDI事件工作,由程序员负责管理协议以及时间处理。 
4 定时器(Timer)接口:为同步音频事件提供对声卡上时间处理硬件的访问。 
5 时序器(Sequencer)接口 
6 混音器(Mixer)接口

声音缓存和数据传输

每个声卡都有一个硬件缓存区来保存记录下来的样本。当缓存区足够满时,声卡将产生一个中断。内核声卡驱动然后使用直接内存(DMA)访问通道将样本传送到内存中的应用程序缓存区。类似地,对于回放,任何应用程序使用DMA将自己的缓存区数据传送到声卡的硬件缓存区中。 
这样硬件缓存区是环缓存。也就是说当数据到达缓存区末尾时将重新回到缓存区的起始位置。ALSA维护一个指针来指向硬件缓存以及应用程序缓存区中数据操作的当前位置。从内核外部看,我们只对应用程序的缓存区感兴趣,所以本文只讨论应用程序缓存区。 
应用程序缓存区的大小可以通过ALSA库函数调用来控制。缓存区可以很大,一次传输操作可能会导致不可接受的延迟,我们把它称为延时(latency)。 为了解决这个问题,ALSA将缓存区拆分成一系列周期(period)(OSS/Free中叫片断fragments).ALSA以period为单元来 传送数据。 

一个缓存区分解成周期,然后是帧,然后是样本。

Over and Under Run

当一个声卡活动时,数据总是连续地在硬件缓存区和应用程序缓存区间传输。但是也有例外。在录音例子中,如果应用程序读取数据不够快,循环缓存区将会被新的 数据覆盖。这种数据的丢失被称为overrun.在回放例子中,如果应用程序写入数据到缓存区中的速度不够快,缓存区将会”饿死”。这样的错误被称 为”underrun”。在ALSA文档中,有时将这两种情形统称为”XRUN”。适当地设计应用程序可以最小化XRUN并且可以从中恢复过来。

 

声卡和 PCM 设备的建立过程

声卡驱动中,一般挂载着多个逻辑设备,看看我们计算机的声卡驱动有几个逻辑设备:

$ cat /proc/asound/devices

嵌入式系统中,通常我们更关心 PCM 和 CTL 这两种设备。

设备节点如下: 

$ ll /dev/snd

 

Codec、Platform、Machine 驱动的组成部分及其注册过程,这三者都是物理设备相关的,频驱动的中间层,由于这些并不是真正的物理设备,故我们称之为逻辑设备。

PCM 逻辑设备,我们又习惯称之为 PCM 中间层或 pcm native,起着承上启下的作用:往上是与用户态接口的交互,实现音频数据在用户态和内核态之间的拷贝;往下是触发 codec、platform、machine 的操作函数,实现音频数据在dma_buffer <-> cpu_dai <-> codec 之间的传输。

 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_card 是 alsa 中最顶层的结构。我们再看看 alsa 声卡驱动的大致结构图(不是严格的 UML 类图,有结构体定义、模块关系、函数调用,方便标示结构模块的层次及关系):

snd_card

snd_cards:记录着所注册的声卡实例,每个声卡实例有着各自的逻辑设备,如 PCM 设备、CTL 设备、MIDI 设备等,并一一记录到 snd_card 的 devices 链表上 
snd_minors:记录着所有逻辑设备的上下文信息,它是声卡逻辑设备与系统调用 API 之间的桥梁;每个 snd_minor 在逻辑设备注册时被填充,在逻辑设备使用时就可以从该结构体中得到相应的信息(主要是系统调用函数集file_operations

2.声卡的创建

声卡实例通过函数 snd_card_create() 来创建,其函数原型:

  1. int snd_card_create(int idx, const char *xid,

  2. struct module *module, int extra_size,

  3. struct snd_card **card_ret)

注释非常详细,简单说下:

  • 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

  • number:0
  • id:PCH
  • shortname:HDA Intel PCH
  • longname:HDA Intel PCH at 0xf7c30000 irq 47

 3.逻辑设备的创建

当声卡实例建立后,接着可以创建声卡下面的各个逻辑设备了。每个逻辑设备创建时,都会调用 snd_device_new() 生成一个 snd_device 实例,并把该实例挂到声卡 snd_card 的 devices 链表上。alsa 驱动为各种逻辑设备提供了创建接口,如下:

DeviceInterface
PCMsnd_pcm_new()
CONTROLsnd_ctl_create()
MIDIsnd_rawmidi_new()
TIMERsnd_timer_new()
SEQUENCERsnd_seq_device_new()
JACKsnd_jack_new()

4.声卡的注册

当声卡下的所有逻辑设备都已经准备就绪后,就可以调用 snd_card_register() 注册声卡了:

  • 创建声卡的 sysfs 设备;
  • 调用 snd_device_register_all() 注册所有挂在该声卡下的逻辑设备;
  • 建立 proc 信息文件和 sysfs 属性文件。

int snd_card_register(struct snd_card *card)     

 // 创建 sysfs 设备,声卡的 class 将会出现在 /sys/class/sound/ 下面

  1. if (!card->card_dev) {

  2. card->card_dev = device_create(sound_class, card->dev,

  3. MKDEV(0, 0), card,

  4. "card%i", card->number);

  5. // 遍历挂在该声卡的所有逻辑设备,回调各 snd_device 的 ops->dev_register() 完成各逻辑设备的注册

  6. if ((err = snd_device_register_all(card)) < 0)

       snd_cards[card->number] = card; // 把该声卡实例保存到 snd_cards 数组中

至此完成了声卡及声卡下的所有逻辑设备的注册,用户态可以通过系统调用来访问这些设备了。

 

5. PCM 设备的创建

最后我们简单描述下 PCM 设备的建立过程:

snd_soc_instantiate_card

snd_pcm_set_ops:设置 PCM 设备的操作接口,设置完成后,在 PCM 设备层即可访问操作底层音频物理设备。 
snd_pcm_new

  • 创建一个 PCM 设备实例 snd_pcm
  • 创建 playback stream 和 capture stream,旗下的 substream 也同时建立;
  • 调用 snd_device_new() 把 PCM 设备挂到声卡的 devices 链表上。

我们再看看 PCM 设备的系统调用:onst struct file_operations snd_pcm_f_ops[2] = {

snd_pcm_f_ops 作为 snd_register_device_for_dev() 的参数被传入,并被记录在 snd_minors[minor] 中的字段 f_ops 中。snd_pcm_f_ops[0] 是回放使用的系统调用接口,snd_pcm_f_ops[1] 是录制使用的系统调用接口。

 

6.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)
 

这个 buffer 中有 4 个 period,每当 DMA 搬运完一个 period 的数据就会出生一次中断,因此搬运这个 buffer 中的数据将产生 4 次中断。ALSA 为什么这样做?因为数据缓存区可能很大,一次传输可能会导致不可接受的延迟;为了解决这个问题,alsa 把缓存区拆分成多个周期,以周期为单元传输数据。

Frames & Periods

敏感的读者会察觉到 period 和 buffer size 在 PCM 数据搬运中扮演着非常重要的角色下面引用两段来自 alsa 官网对 Period 的详细解释:Retrieved from “http://alsa.opensrc.org/Period”

 buffer_size = period_size * periods 
- period_bytes = period_size * bytes_per_frame 
- bytes_per_frame = channels * bytes_per_sample

简单说下 Frame 和 Period 要点:

  • Frame:帧,构成一个完整的声音单元,它的大小等于 sample_bits * channels;
  • Peroid:周期大小,即每次 dma 运输处理音频数据的帧数。如果周期大小设定得较大,则单次处理的数据较多,这意味着单位时间内硬件中断的次数较少,CPU 也就有更多时间处理其他任务,功耗也更低,但这样也带来一个显著的弊端——数据处理的时延会增大。

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。

因此我们可以用定时器来模拟这种硬件中断:

  1. 触发dma搬运数据时,启动定时器开始计时;
  2. 当定时到 1 * period_size / sample_rate,这时 I2S 已传输完一个周期的音频数据了,进入定时器中断处理:调用snd_pcm_period_elapsed() 告知 pcm native 一个周期的数据已经处理完毕了,同时准备下一次的数据搬运;
  3. 继续执行步骤 1…

为了更好保证数据传输的实时性,建议采用高精度定时器 hrtimer。

两家芯片在传输音频数据时需要用定时器模拟周期中断,一是 MTK 的智能手机处理器,二是 Freescale 的 i.MX 系列处理器。后者已经合入 Linux 内核代码,具体见:sound/soc/imx/imx-pcm-fiq.c

// 定时器中断处理例程
static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)

{
    ...
 
    /* If we've transferred at least a period then report it and
     * reset our poll time */
    if (delta >= iprtd->period) {
        snd_pcm_period_elapsed(substream); // 告知 pcm native 一个周期的数据已经处理完毕
        iprtd->last_offset = iprtd->offset;
    }
 
    hrtimer_forward_now(hrt, ns_to_ktime(iprtd->poll_time_ns)); // 重新计时,poll_time_ns:I2S 传输一个周期的数据所需的时间
    return HRTIMER_RESTART;
}
 
// 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->size = params_buffer_bytes(params);    // dma 缓冲区大小
    iprtd->periods = params_periods(params);      // 周期数
    iprtd->period = params_period_bytes(params) ; // 周期大小

    iprtd->offset = 0;
    iprtd->last_offset = 0;
    iprtd->poll_time_ns = 1000000000 / params_rate(params) *
                params_period_size(params);       // 计算 I2S 传输一个周期的数据所需的时间
    snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); // 设置 dma 缓冲区
 
    return 0;
}
 
// 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;
 
    switch (cmd) {
    case SNDRV_PCM_TRIGGER_START:
    case SNDRV_PCM_TRIGGER_RESUME:
    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
        atomic_set(&iprtd->running, 1);
        hrtimer_start(&iprtd->hrt, ns_to_ktime(iprtd->poll_time_ns), // 准备传输数据,启动定时器,开始计时
              HRTIMER_MODE_REL);
        if (++fiq_enable == 1)
            enable_fiq(imx_pcm_fiq); // 开始 dma 传输
 
        break;
 
    case SNDRV_PCM_TRIGGER_STOP:
    case SNDRV_PCM_TRIGGER_SUSPEND:
    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
        atomic_set(&iprtd->running, 0);
 
        if (--fiq_enable == 0)
            disable_fiq(imx_pcm_fiq); // 停止 dma 传输
 
        break;
    default:
        return -EINVAL;
    }
 
    return 0;
}
 

 

 

参考:

VoIP网络中虚拟声卡的研究及应用  http://www.docin.com/p-1406014438.html

linux 虚拟声卡 dummy 创建 http://cadenwu.blog.chinaunix.net/uid-20768928-id-5736361.html

ALSA(Advanced Linux Sound Architecture)声卡编程介绍及实例 https://blog.csdn.net/lell3538/article/details/62057159

Linux ALSA 音频系统:逻辑设备篇   https://blog.csdn.net/sunjing_/article/details/79145402

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

虚拟声卡 的相关文章

  • Spring Bean 创建过程

    0 通常 xff0c 无论是DispatcherServlet ContextLoaderListener还是ClassPathXmlApplicationContext xff0c 首次实例化bean的入口并不是在每次调用getBean的
  • MySQL DataSource 性能对比(2015-8-19)

    1 本地性能测试耗时 xff08 一 xff09 共同条件 xff1a 测试程序与数据库在同一台主机上 xff0c 各DataSource均采用默认配置 xff0c 每个线程循环1000次 xff0c 查询语句为select from ta
  • MySQL 乐观锁 简例

    乐观锁与悲观锁不同的是 xff0c 它是一种逻辑上的锁 xff0c 而不需要数据库提供锁机制来支持 当数据很重要 xff0c 回滚或重试一次需要很大的开销时 xff0c 需要保证操作的ACID性质 xff0c 此时应该采用悲观锁 而当数据对
  • HTML5 Canvas 初步:字符串,路径,背景,图片

    HTML5中新增了 lt canvas gt 画布标签 xff0c 通过它 xff0c 可以使用JavaScript在网页中绘制图像 lt canvas gt 标签在网页中得到的是一个矩形空白区域 xff0c 可以通过width和heigh
  • CSS 伪类与伪元素

    CSS的元素选择器除了根据id xff08 xff09 class xff08 xff09 属性 xff08 xff09 选取元素以外 xff0c 还有很重要的一类 xff0c 就是根据元素的特殊状态来选取元素 它们就是伪类和伪元素 跟id
  • 阿里巴巴2022届应届生招聘 阿里云存储基础技术 研发&测试&算法

    学弟学妹们好 xff0c 我们是阿里云存储基础技术团队 xff0c 正在进行2022届校招 团队业务核心 xff0c 团队氛围nice xff0c 对于每位实习同学都会配师兄专职辅导 本次招聘为部门直招 xff0c 简历直接推给Leader
  • CSS3 动画效果总结

    CSS3添加了几个动画效果的属性 xff0c 通过设置这些属性 xff0c 可以做出一些简单的动画效果而不需要再去借助JavaScript CSS3动画的属性主要分为三类 xff1a transform transition以及animat
  • Javassist学习总结

    要想将编译时不存在的类在运行时动态创建并加载 xff0c 通常有两种策略 xff1a 1 动态编译 2 动态生成二进制字节码 xff08 class xff09 对于第二种策略 xff0c 实际上已经有诸多比较成熟的开源项目提供支持 xff
  • 程序员的酸甜苦辣——告别Coding

    程序员的酸甜苦辣 告别Coding lt script language 61 34 javascript 34 type 61 34 text javascript 34 gt document title 61 34 程序员的酸甜苦辣
  • Vue学习笔记7 - 在Vscode中配置Vetur,ESlint,Prettier

    俗话说得好 xff0c 工欲善其事必先利其器 xff0c 想要熟练开发Vue项目 xff0c 一个好的开发环境就是必不可少了 xff0c 这里我就选用了vscode作为开发工具 xff0c 毕竟vscode免费 xff0c 还跨平台 xff
  • 我的所有的浏览器被hao123 挟持了,终极解决方案

    参考https www cnblogs com BensonLaur p 13731310 html 解决 最近我给我的老电脑安装系统在我这台电脑做了启动盘 xff0c 之后我的每个浏览器点开就是hao123 的主页 xff0c 而后我用了
  • 字节跳动2-1 三轮大数据方向算法20220330

    新鲜出炉 xff0c 大数据的总监 xff0c 一上来什么都没问 xff0c 让我写一个非递归后续遍历 很不好意思让他打脸了 xff0c 这个题我做过5片了 xff0c 理解上还是很深刻的 我就想对他说为啥面试连自我介绍都不给我 xff0c
  • 字节跳动抖音电商2-2 算法 20220331

    题目 xff1a n 61 61 nums length 1 lt 61 n lt 61 104 0 lt 61 nums i lt 61 n nums 中的所有数字都 独一无二 给定一个包含 0 n 中 n 个数的数组 nums xff0
  • java 线程池执行流程源码讲解

    threadPoolExecutor execute 执行过程 public void execute Runnable command if command 61 61 null throw new NullPointerExceptio
  • spring boot 打包成jar 包在发布到服务器上

    pom xml文件 lt xml version 61 34 1 0 34 encoding 61 34 UTF 8 34 gt lt project xmlns 61 34 http maven apache org POM 4 0 0
  • JAVA Apache POI 之sax 解析10万级大数量数据

    第一步让我们来看看我们的大量数据的excel 文件 img src https img blog csdn net 20170330162913720 watermark 2 text aHR0cDovL2Jsb2cuY3Nkbi5uZXQ
  • 百度2014校园招聘笔试题武汉站三道算法设计题

    百度2014校园招聘笔试题武汉站三道算法设计题 1 给定任意一个整整数 求比这个数大且最小的不重复数 就是相邻两位不同 xff0c 例如1231 如1101就是重复数 解 xff1a 思路 xff1a 每次将给定的值加上1 xff0c 然后
  • spring data jpa 想使用EntityManager 对sql 进行处理四种方式(第四种本人改写的)

    下面看看主体的一个类 xff1a package com chinait service impl import java util List import javax persistence EntityManager import ja
  • Swagger2 (3) 集成easymock 生成mock 测试数据

    1 什么是 easy mock 2 可以集成swagger 3 我们来玩一下 首先你需要一个swagger 服务 xff1a 其次我们需要一个easy moke 网站账号 xff1a 接下来选择一个项目 点击编辑项目的配置 配置好项目信息

随机推荐

  • 用canvas做视频截图遇到的坑(已填坑)

    最近负责了一个后台功能的扩展 xff0c 因为没有前端 xff0c 所以客串了一把前端 xff0c 需求的内容是做一个视频截图的功能 xff0c 这期间遇到了canvas 的跨域问题 xff0c Uncaught DOMException
  • java 8 list.stream().collect Collectors.toMap 重复key 值处理

    问题描述 xff1a list 转 map 时 xff0c 首先看 两个phoneAuthUpdater 的key 都是 1 xff0c key 如果重复 xff0c 则会报这个错 可以选择第二种方法来解决 这种方式可以解决上面的问题 xf
  • mysql 时间格式转换,时区转化

    首先 xff0c 为了更好的展示 xff0c 我先把数据库里面存储的数据展示一下 xff0c 是如下图 xff1a 1 时间格式转换 时间 39 2019 01 22 15 45 06 39 转换成 unix 时间戳 select UNIX
  • 5.4 Stream Buffer

    Stream Buffer 是一种广义 Cache xff0c 主要功能是避免因为预读而造成的 Cache Pollution 问题 当采用该机制时 xff0c 处理器可以将预读的数据序列放入 Stream Buffer 中而不是放入 Ca
  • VxWorks的信号量机制分析

    VxWorks 的信号量机制分析 VxWorks 信号量是提供任务间通信 同步和互斥的最优选择 xff0c 提供任务间最快速的通信 也是提供任务间同步和互斥的主要手段 VxWorks 提供 3 种信号量来解决不同的问题 二进制信号量 xff
  • Linux系统内存、磁盘占用情况查询

    查看磁盘占用空间 df h 显示所有磁盘的使用情况 xff0c 包括磁盘的总大小 已用空间 可用空间和文件系统类型等 查看运行内存的占用情况 free m 查看进程 1 strong span style color fe2c24 ps s
  • 关于c语言的tcp通讯详细讲解

    目录 1 TCP概览 1 1 TCP基本特征 1 2 TCP通信流程基本原理 2 TCP编程的函数接口说明 3 TCP通讯测试代码 1 TCP概览 TCP全称 Transmition Control Protocol xff0c 即 xff
  • python watchdog:监控文件系统事件的Python库

    python watchdog xff1a 监控文件系统事件的Python库和shell工具 watchdog用来监控指定目录 文件的变化 xff0c 如添加删除文件或目录 修改文件内容 重命名文件或目录等 xff0c 每种变化都会产生一个
  • 关于c语言的udp通讯详细讲解

    目录 1 UDP简介 2 UDP通信流程 3 UDP的函数接口说明 4 UDP通讯测试代码 1 UDP简介 UDP全称 User Datagram Protocol xff0c 即 xff1a 用户数据报协议 是面向无连接的协议 通常 xf
  • python中关于Opencv中关于矩形的函数总结

    最近处理图像 xff0c 以前用的都是matlab xff0c 现在入手python比较慢 xff0c 这几天看到了很多命名和功能相似的函数 xff0c 作个记录总结一下 只是为了能够看下函数知道它是做什么的 xff0c 因此不会对其用法说
  • 在虚拟机中安装Ubuntu-Docker

    在虚拟机中安装Ubuntu Docker 第一步 安装虚拟机 1 安装虚拟机 xff0c 测试网络联网 图 1 安装ubuntu 图 2 设置系统时间 2 建立快照 建立快照 xff1a 快照001 安装成功 第二步 内核更新 可以通过do
  • Java中的toString()方法

    一 toString 方法介绍 toString 方法是 Object 类中的方法 xff0c toString 方法源代码如下 xff1a 1 getClass getName 返回类的全类名 包名 43 类名 2 Integer toH
  • 二十八. Semaphore的使用详解

    前言 Semaphore的官方注释如下 计数信号量 从概念上讲 xff0c 信号量维护一组许可证 xff08 permits xff09 通常 xff0c 每次调用Semaphore acquire方法时如果已经没有许可证 xff0c 则会
  • FreeRTOS vTaskDelay 卡死

    在Task中使用动态方法创建任务后 xff0c 在任务中调用vTaskDelay卡死 xff0c 然后排查问题 xff0c 定时器工作正常 xff0c 如果不使用vTaskDelay也可以一直运行 解决办法 xff0c 改成在main方法中
  • 图像为什么能相减

    前面提到 xff0c 我们可以把图像看作一个函数 xff0c 函数和函数之间是可以相加的 f x y 61 I 1 x y 43 I 2 x y 那么 xff0c 既然函数可以相加 xff0c 函数也是可以相减的 所以 xff0c 图像之间
  • 分析评估和定位声音质量

    64 author wangdaopo 64 email 3168270295 64 qq com 影响音频质量和稳定性的因素 音质好坏的评价 xff0c 响度 音高 音色 xff0c 测试 xff0c 你的语音引擎是基本可用的 xff0c
  • Linux下Clang-format代码格式化

    64 author wangdaopo 64 email 3168270295 64 qq com 1 Clang format 代码格式化介绍 平时团队进行合作的时候需要注意代码的格式 xff0c 虽然很难统一每个人的编码风格 xff0c
  • OOM问题预防和排查内存泄漏及解决方法

    64 author wangdaopo 64 email 3168270295 64 qq com 理解了这个算法我们就理解了为啥 MySQL 躺着也能中枪了 xff0c 因为它的体积总是最大 xff08 一般来说它在系统上占用内存最多 x
  • API调用次数限制实现

    API调用次数限制实现 在开发接口服务器的过程中 xff0c 为了防止客户端对于接口的滥用 xff0c 保护服务器的资源 xff0c 通常来说我们会对于服务器上的各种接口进行调用次数的限制 比如对于某个 用户 xff0c 他在一个时间段 x
  • 虚拟声卡

    一 虚拟声卡是什么 xff1f 虚拟声卡是一种软件产品 xff0c 它只对声音数字信号进行处理 虽然它不能产生声音 xff0c 但可以用来实现声音的传输 存储或混音等功能 1 xff09 虚拟声卡工作原理 xff1a 虚拟声卡通过软件技术实