掌握音频开发基础知识

2023-05-16

文章目录

      • 基本概念
      • 几种CODEC介绍
      • 实时调度相关
        • 缓冲区
          • 两种类型
          • 编写要点
          • 遇到的问题
        • 解码能力的自适应
        • 混音模块
        • 回声消除的延时控制
        • 能量统计
        • 双声道支持
        • ALSA设备
      • 代码相关

基本概念

  • 采样率(Hz) : 每秒去取样本的个数,eg: 48000Hz也就是每秒取样本48000个
  • 比特率(bps) :一秒钟传送多少个bit
  • 采样位数 : 每个样本占多少个bit,一般都是16bit也就是两个字节
  • 编/解码周期 : 多久编/解码一次,对应不同的数据量
  • 编解码能力 : 就同视频的h264,h265,VP8,VP9一样,音频也有多种编解码能力,主要使用到的有 alaw,ulaw,g722,aacld,opus等

几种CODEC介绍

  • alaw/ulaw : 即是g711a与g711u,主要用在电话中,采样率为8k,主要是将16位数据压缩为8位,可得比特率为64kbps
  • g722 : 采样率16K,相对于g711来说能处理的音频信号带宽更大,比特率仍为64kbps
  • aacld : (Advanced Audio Coding - Low Delay)多种采样率,我们主要用32k,这个编码主要特点在于高压缩比与低延迟上,比特率可以自行控制
  • opus : Opus编码是由silk编码和celt编码合并在一起,可以实现低延迟也可以实现高保真,我们主要使用48k采样率,libopus中同样有inbandfec功能,可以在丢包情况下预测还原语音,功能十分强大
    对于不同能力的评估,可以在编码成相同码率的文件后解码回来,然后使用PESQ与编码前的原始文件比较

PESQ(Perceptual evaluation of speech quality) 即:客观语音质量评估。 ITU-T P.862建议书提供的客观MOS值评价方法。

实时调度相关

会议系统中的采集,编码,解码,混音等部分,是不同的线程,需要配合起来才能工作。这些线程之间就是通过环形缓冲区连接起来实现实时调度的。如图

这里写图片描述
这样,每个线程只需要处理自己的事情并从缓冲区读取/写入数据即可。(缓冲区满时写入,以及缓冲区空时读取,处理不好依然会造成阻塞)。

缓冲区

两种类型
  • 块缓存:缓冲区分配的读写单位为固定大小的块,用以读取或者写入一帧音频数据(一帧原始音频数据长度,由采样周期(处理周期)决定),这里按照最大分配,一块缓存可以存放48K 16bit 50ms长度的数据,即采样周期50ms.
  • 点缓存:缓冲区分配的读写大小不固定,可以精确到字节,主要用来处理写入与读取缓冲区大小不匹配的情况,例如,AACLD的编解码周期为16ms,但是混音周期为10ms或者20ms(统一)。如果用上面的块缓冲很难处理这种情况

编写要点

封装好初始化,读取,写入,以及调试四个部分的函数

  • 初始化 : 主要是给定参数:缓冲区大小(缓冲块的个数,块的大小等),主要是分配空间,初始化计数变量,指针
  • 读取/写入 : 获取缓冲区buf 以及归还缓冲区buf(主要是指针操作,以及计数变量累加)
  • 调试 : 主要是用于日后出问题时的调试,主要就是打印当前环形缓冲区的读写指针位置,读写次数等等
/**
 * 块缓冲区接口示例
 */
BlkBuffHandle createLoopBuffer(int size, int number);  //初始化缓存块大小与缓存块个数
int destroyLoopBuffer(BlkBuffHandle bfhandle);         //销毁
void *getReadingLoopBuffer(BlkBuffHandle bfhandle);    //尝试获取缓冲区内容,返回一个缓存块指针
int putReadingLoopBuffer(BlkBuffHandle bfhandle);      //读取完后归还缓存块,内部实际为读指针移动
void *getWritingLoopBuffer(BlkBuffHandle bfhandle);    //尝试获取缓存区未用空间,返回一个缓存块指针
int putWritingLoopBuffer(BlkBuffHandle bfhandle);      //写入完后归还缓存块,内部实际为写指针移动
/**
 * 点缓冲区接口示例
 */
 
/** 
 * 初始化点缓存区,根据采样率和帧数,以及默认帧长度确定大小,
 * 如传入 48000,10,帧长度为10ms,每个音频样本为2bytes,
 * 则缓冲区总大小为 48000Hz*(10ms/1000ms)*10*2  (bytes)  
 */
PtBuffHandle createPointBuffer(int smprate,int frame_num); 
int destroyPointBuffer(PtBuffHandle bfhandle); //销毁缓冲区
void *getReadingPointBuffer(PtBuffHandle bfhandle,int readsize);//尝试读取指定长度,返回起始地址
int putReadingPointBuffer(PtBuffHandle bfhandle, int readsize);//读取完后归还,传入实际读取的长度以更新指针
void *getWritingPointBuffer(PtBuffHandle bfhandle, int writesize);//尝试写入指定长度,返回起始地址
int putWritingPointBuffer(PtBuffHandle bfhandle, int writesize);//写入完后归还,传入实际写入长度以更新指针

/* 在获取失败的情况下,返回指针都为 NULL,需要再次获取 */

遇到的问题
  1. 获取缓冲区指针陷入死循环问题
    通常需要获取到缓冲区的数据才能进行下一步的处理和操作,这么做必然少不了循环操作,在获取到时返回。这么做的潜在风险是,获取不到时卡在循环中出不来,无法响应其他模块发出的消息等。有下面几点
  • 在每次获取失败后都要usleep(1000)一下,让出时间片使其他线程能够获取到锁,否则死循环快速执行其他线程也无法读取/写入数据

  • 获取缓冲区要有超时次数限制,再超过指定的次数后需要进行某种处理并退出循环,不能无限卡在某个小循环中

  • 在某些需要死等的情况下要给循环使用标志位,如while(bEnableFlag){...} 来避免死循环。

/* 设置超时次数 */
        while (1)
        {
            outbuf = (u8 *)getWritingPointBuffer(param->ptbuff,dec_samples*2);
            if(outbuf || try_put_time <= 0)
            {
                break;
            }
            try_put_time--;
            gettimeofday(&bf_sleep, NULL); //为了测试usleep(1000)到底能不能达到1MS
            usleep(1000);
            gettimeofday(&aft_sleep, NULL);//为了测试usleep(1000)到底能不能达到1MS
            /* 这里当初无论如何都无法实时调度,调试时候发现usleep(1000)也达到了10MS,达不到实时调度要求
               最后重新编译内核改变时钟频率(100Hz-->1000Hz)后才解决的问题 */
       }
/* 设置循环条件 */
        while (param->cycle_enable)
        {
            outbuf = (u8*)getWritingLoopBuffer(param->blkbuff);
            if (outbuf)
            {
                break;
            }
            usleep(1000);
        }

解码能力的自适应

这里解码器的解码能力可以在运行过程中动态变化而不用重启程序,再RTP传输的数据中添加了5字节Header,包含了三个内容:包序号,解码能力Index,包长度。

  1. 两次收到包序号的连续与否决定是否使能FEC(如果有该能力的话)
  2. 根据解码能力Index来重新初始化解码器,同时改变采样周期时间,写数据长度,重置点缓冲区以及通知混音模块能力变化等
  3. 包长度决定了块缓存中读取多少有效数据

混音模块

组成: 重采样 ->混音->重采样
混音模块需要获取多路缓冲区的数据并处理后放入后级缓冲区中,这里需要做好处理,如,使用采集驱动混音工作,这里死等采集数据后才能工作,对于其他路的音频,如果获取不到默认为0来处理,对于后期缓冲区,同样,能获取到则写缓冲区,获取不到则不写。通过这种方式保证混音模块不会阻塞在某一路上。

  • 大循环中使用消息队列接收来自DEC与ENC的消息,如DEC/ENC能力的变化(只有采样率变化才会影响到MIX重采样处理)

  • 多进多出的具体处理(16进16出为例):

  1. 使能一路
  2. 获取音频数据
    其中mic数据为死等处理,以MIC驱动Mix模块工作,其他路音频等待3次,获取不到读缓存则为0数据,获取不到写缓存则不写。
  3. 混音处理
    ret = Audio_Scheduler_TransOneFrame(s8 **inaddr,s8 **outaddr);
//from mix_thread_func()
/* 使能一路的主要操作 */
int Audio_Sheduler_enable_one(u8 index,u16 decSmprate,u16 encSmprate)
{
    //pthread_mutex_lock(&Audio_Scheduler_Mutex);

    /*由传输模块决定直接使能 index 路*/
    if (!sg_stInfo.enable_flag_arr[index])
    {
        sg_stInfo.enable_flag_arr[index] = 1; //置位,在混音的时候循环扫描标志位决定是否参与混音
        Audio_Sheduler_Change_decSmprate(index, decSmprate);//初始化相应的重采样实例句柄
        Audio_Sheduler_Change_encSmprate(index, encSmprate);
        return 0;
    }

回声消除的延时控制

回声消除的原理是:将采集信号(已经录入了远端声音)与参考信号(播放出来的远端声音)传入后,做一些处理,还原为只有本地声音的过程,这里,参考信号和采集信号采集到回声的时间一定要对应上,否则回声消除会出现问题,我们需要将声音播放出来到被MIC采集到的时间做一下记录,统计一下延迟需要多久,记为时间T,然后就将延迟了T时间的参考信号发送给3A模块,以实现处理。
流程图为:

mic ptBuf_in 3A ptBuf_ref speaker ptBuf_out 采集 送采集信号 送参考信号 3A参考信号缓存 输出处理后的信号 mic ptBuf_in 3A ptBuf_ref speaker ptBuf_out

能量统计

这里已经有了简单测试过的代码,用来统计出多路情况下语音能量较大的几路(能量排序),将来用于多画面MCU会议时动态选择显示出声音最大的几路画面,同时混音也可以只混入显示出画面那些路音频。
这里的能量计算取自MIX模块一路音频中一帧绝对值和的均值。

双声道支持

目前的音频模块处理都是单声道的,将来需要支持双声道,只需要在缓冲区中再申请一块相同大小的空间。

//例如该实例中,再添加void **paddr_chn2
typedef struct {
	int size;		// 队列中每个buffer的长度
	int number;		// 队列中的buffer总数
	int readid;		// 读指针(buffer序号)
	int writeid;	// 写指针(buffer序号)
	int readcnt;	// 读操作计数器
	int writecnt;   // 写操作计数器
	void **paddr;	// 队列中每个buffer的内存起始地址
	/**
	 * 例如此处添加void **paddr_chn2;用来存储声道2数据,他们共享上面所有的计数变量,以及指针偏移量等等,
	 */
	void **paddr_chn2; 
	/**************************************/
	pthread_mutex_t mutex;  // 读写指针的互斥锁
}T_LoopBuffer;

ALSA设备

为了避免驱动出现XRUN错误,对于此类设备一定要按照设定的周期及时读写.同时程序中要对USB-ALSA设备进行热插拔检测,设备不存在时往缓冲区写零以保证混音模块继续运行(采集驱动混音)

代码相关

  • 为了日后扩展某种音频能力方便,所有的编解码器采用同一套接口,内部通过增加switch分支即可。
typedef void *(*DEC_INIT_FUNC)(const T_decoder_param *);
typedef s32 (*DEC_FUNC)(void*, u8*, s32, u8*, s32*);
typedef void (*DEC_DESTROY_FUNC)(void*);

typedef void *(*ENC_INIT_FUNC)(const T_encoder_param *);
typedef s32 (*ENC_FUNC)(void*, u8*, s32, u8*, s32*);
typedef void (*ENC_DESTROY_FUNC)(void*);

typedef struct decoder_func{
    void* _handle;
    //DEC_INIT_FUNC       audio_decoder_init;
    DEC_FUNC            decode;
    DEC_DESTROY_FUNC    destroy;
} T_decoder_func;

typedef struct encoder_func{
    void* _handle;
    //ENC_INIT_FUNC       audio_encoder_init;
    ENC_FUNC            encode;
    ENC_DESTROY_FUNC    destroy;
} T_encoder_func;

void* audio_decoder_init(const T_decoder_param* dec_param)
{
    
    T_decoder_func *p_stfunc = NULL;
    p_stfunc = (T_decoder_func *)malloc(sizeof(T_decoder_func));
    if(!p_stfunc)
    {
        printf("MALLOC FOR T_decoder_func error\n");
        return NULL;
    }
    memset(p_stfunc, 0, sizeof(T_decoder_func));
    DEC_INIT_FUNC p_dec_init;
    
    if(dec_param->decoder_index == CODEC_INDEX_G722) //g722
    {
        //local coder in ffmpeg
        p_dec_init = &local_decoder_init;
        p_stfunc->decode =&local_decoder_decoding;
        p_stfunc->destroy = &local_decoder_destroy;
    }
    else
    {
        //third coder
        switch(dec_param->decoder_index)
        {
            
            case CODEC_INDEX_AACLD:
            {
                p_dec_init = &aacld_decoder_init;
                p_stfunc->decode =&aacld_decoder_decoding;
                p_stfunc->destroy = &aacld_decoder_destroy;
            }break;
            case CODEC_INDEX_OPUS:
            {
                p_dec_init = &OpusDec_init;
                p_stfunc->decode = &OpusDec_decoding;
                p_stfunc->destroy = &OpusDec_destroy;
            }break;
            case CODEC_INDEX_ULAW:
            case CODEC_INDEX_ALAW:
            {
                p_dec_init = &g711_decoder_init;
                p_stfunc->decode =&g711_decoder_decoding;
                p_stfunc->destroy = &g711_decoder_destroy;
                
            } break;
            default:
                break;
        }
    }
    p_stfunc->_handle = p_dec_init(dec_param);
    if(!p_stfunc->_handle)
    {
        free(p_stfunc);
        p_stfunc =NULL;
    }
    
    return p_stfunc;
}

s32 audio_decoder_decoding(void *handle,u8 *inbuf, s32 length, u8 *outbuf, s32 *outlen )
{
    T_decoder_func *phandle = (T_decoder_func *)handle;
    return phandle->decode(phandle->_handle,inbuf, length, outbuf,outlen);
}
void audio_decoder_destroy(void *handle)
{
    T_decoder_func *phandle = (T_decoder_func *)handle;
    phandle->destroy(phandle->_handle);
    free(phandle);
}
  • alsa设备操作:
  • 检测USB-ALSA设备是否存在
 int soundcard_exist()
{
    const char *dev_name = "/dev/snd/controlC0";
    if(!access(dev_name,F_OK))
    {
        return 1;
    }
    else
        return 0;
}

用于在程序中实现usb-alsa设备的动态热插拔检测,插上设备后无需重启程序即可使用。
ALSA设备特别容易出现XRUN错误,比较好的办法就是按照设定的周期读/写设备(必须及时)。

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

掌握音频开发基础知识 的相关文章

  • 无人驾驶——激光雷达篇

    激光雷达技术简介 无人驾驶技术是多项技术的集成 xff0c 包括传感器 定位与深度学习 高精地图 路径规划 障碍物检测与规避 机械控制 系统集成与优化 能耗与散热管理等 无人车系统的感知端由不同的传感器组成 xff0c 其中包括GPS xf
  • simulink搭建简单的陷波滤波器

    simulink搭建简单的陷波滤波器 陷波滤波器的设计模型的搭建通过matlab自带软件设计的滤波器与simulink自带滤波器模块对比 陷波滤波器的设计 具体设计方法可参见这篇 xff1a 算法学习笔记之50HZ陷波滤波器设计 下面给出各
  • docker——常用指令

    docker pull url 从url拉取容器 docker run d url 后台运行镜像 xff0c 如果不存在该镜像 xff0c 则会自动下载 docker exec it 容器id bash 进入镜像容器 xff0c 容器的id
  • Realsense、kinect在ROS环境下的骨骼识别(基于NuiTrack)

    ps 出售我购买的nuitrack liecense xff0c 支持月付 在弄不明白ROS Kinectic之后的openNI之后终于还是向Nuitrack骨骼识别屈服了 xff1a 那么先看看NuiTrack是啥 xff1a https
  • 深度图像帧差法处理以及CV16UC1深度图像的存储方式

    最近为了拾取桌面上的任务目标 xff0c 采用了实时图像与背景图像相减的方法来进行 xff0c 因为采用彩色图像相减会有影子的干扰 xff0c 所以采用了深度图像 但是深度图像的Mat是CV16UC1格式的 矩阵内部数据采用uint 16格
  • Couldn't find executable named joy_node below /opt/ros/kinetic/share/joy解决方法

    最近想用joy node这个节点 xff0c 然后就通过 sudo apt get install ros kinetic joy 去安装 xff0c 然后运行rosrun joy joy node时候一直错误显示Segmentation
  • ros修改map_server地图发布的map关联的坐标系frame_id(多机器人联合建图用)

    帮师弟做多机器人联合建图的时候 遇到了map的坐标系问题如下 map server发布的 map话题包含了frame坐标系关联 想要正确的让多个机器人共同建图导航需要修改 map话题以及其绑定的frame坐标系 防止机器人之间的相互冲突 下
  • ROS传输图像带宽不够用的解决方法(realsenseD415压缩图像)

    最近在做图像的深度学习识别 xff0c 但是移动机器人上的电脑配置不够 xff0c 只能用我的电脑远程的去处理图像 xff0c 但是遇到了严重的带宽瓶颈 xff0c 按照我的电脑150Mbps的无线网卡来算 xff0c 每秒的极限传输速度就
  • dockerfile配置运行

  • 气压计高度融合—卡尔曼滤波

    实验平台 xff1a 自制飞控板 xff0c STM32F407主控 xff0c 传感器 xff1a MPU6050 MS5611 正文 xff1a 前几天看了这篇文章 xff0c 做了气压计的高度融合 http www zxiazai c
  • GD32F303移植FreeRTOS

    GD32F303移植FreeRTOS 一 移植环境 系统 xff1a WIN7 MDK xff1a keil v5 26 开发板 xff1a GD32F303C EVAL 固件库 xff1a V1 0 2 FreeRTOS版本 xff1a
  • FreeRTOS静态创建任务

    一 静态方式创建任务和删除任务 1 gt 测试环境 系统 xff1a WIN7 MDK xff1a keil v5 26 开发板 xff1a GD32F303C EVAL 固件库 xff1a V1 0 2 FreeRTOS版本 xff1a
  • python实现smote处理正负样本失衡问题

    机器学习中难免遇到正负样本不平衡问题 xff0c 处理办法通常有梁总 xff0c 一 xff1a 过采样 xff0c 增加正样本数据 xff1b 二 xff1a 欠采样 xff0c 减少负样本数据 xff0c 缺点是会丢失一些重要信息 sm
  • echarts 饼图hover效果,饼图中间显示自定义信息

    option 61 tooltip show true trigger 39 item 39 position 39 35 39 39 32 39 backgroundColor 39 implements 39 textStyle col
  • MATLAB在线工具

    在线Matlab工具 xff0c 不用安装matlab了 xff0c 里面的语法几乎和matlab相同 matlab网页版 xff1a 1 octave online http octave online net 2 matlab onli
  • Apache IoTDB下载与安装

    1 中文官方文档 xff1a https iotdb apache org zh 2 下载地址 xff1a https iotdb apache org zh Download 发行版本踩了个小坑 xff1a 1 0 0版本启动时如果作为单
  • 【游戏开发】游戏开发书籍汇总

    1 游戏设计的艺术 2 游戏设计的100个原理 3 我在美国学游戏设计 4 游戏新手村 xff1a 从零开始做游戏 5 Directx游戏开发终极指南 6 Windows游戏编程大师技巧 7 快乐之道 xff1a 游戏设计的黄金法则 人类的
  • 【获奖公布】“我的2016”主题征文活动

    还记得2015的年末 xff0c 2016的新年伊始 xff0c 你给自己定下的目标 xff0c 对自己许下的诺言么 xff1f 时光荏苒 xff0c 一年又在指缝间溜走了 xff0c 离2016的结束还剩十多天 xff0c 在接下来的这十
  • Dockerfile介绍与指令解析

    一 Dockerfile介绍 镜像是容器的基础 xff0c 每次执行docker run的时候都会指定哪个镜像作为容器运行的基础 我们之前的例子都是使用来自docker hub的镜像 xff0c 直接使用这些镜像只能满足一定的需求 xff0

随机推荐

  • Kubernetes快速上手指南,让你所见即所得

    版权声明 xff1a 本文为 ABC实验室 原创文章 xff0c 版权所有 xff0c 侵权必究 xff01 编者语 Kubernetes作为新一代云计算平台 xff0c 自2014年开源以来得到快速发展 xff08 2016年 xff09
  • 使用github管理科研文献

    使用github管理科研文献 一 准备工作 xff1a 二 建立远程科研文献库三 远程科研文献数据库的维护四 其他PC客户端的访问 每个科研工作者都需要建立自己的科研文献库 以楼主本人为例 xff0c 我通常在实验室的台式机上完成科研文献的
  • RBF神经网络逼近在线自适应控制(MATLAB实现之S函数模块分析)

    上次用了参考模型的方法用RBF神经网络试了一下放到自适应控制模型上 xff0c 其实跟踪效果还可以 xff0c 放大了有点不舒服就是了 xff0c 差了一点点 xff0c 然后看了看书的第四章 xff0c 知道采用梯度下降法调整神经网络权值
  • 基于RBF神经网络的Flexible Robot自适应控制(论文笔记)

    上一篇笔记 https blog csdn net qq 24182661 记录的是第一篇论文2015 Continuum Robots for Medical Applications A survey的论文笔记 xff0c 主要做的笔记
  • Golang 解析xml文件标签带冒号( : )解决方案

    背景 xff1a 我们有项目需要使用golang语言解析rabbitmq xml 并把里面的内容解析出来 xff0c 但是在解析的时候遇到了问题 xff0c 最后通过google搜索 xff0c 在stackoverflow上找到了解决方案
  • 【Python】Python中 在函数内部对函数外的变量进行操作

    在Python中 xff0c 如果想函数内部对函数外的变量进行操作 xff0c 有一些问题 xff08 一些在Java xff0c C中再正常不过的操作这里就不行 xff09 正常情况下 xff0c 在函数外定义的变量是可以直接在函数体内部
  • 关于proteus中串口发送数据与实际不符的问题(如发00h,收80h)

    工程实训要用到串口 xff0c 51单片机 xff0c 串口工作方式一 xff0c 只发不接受 在proteus中用VIRTUAL TERMINAL xff08 虚拟终端 xff09 监视串口发送数据 现象 xff1a 不论是用虚拟终端还是
  • Jetson TX2的各种坑.md

    最近在使用Jetson TX2 在跑实验 xff0c 然后遇到下面问题 xff0c 做笔记 xff0c 记录一下 内存出错无法 xff0c 中断 出现下面那种错误 2019 01 11 19 41 46 959970 E tensorflo
  • 基于STM32的FreeRTOS开发(1)----FreeRTOS简介

    为什么使用freertos FreeRTOS 是一个免费和开源的实时操作系统 xff0c 它主要用于嵌入式系统 它非常轻量级 xff0c 可以在很小的硬件资源上运行 xff0c 因此非常适合在限制硬件资源的嵌入式系统中使用 FreeRTOS
  • 获奖公布 | 征文——从高考到程序员

    每年的这几天 xff0c 空气中总会弥漫着紧张的味道 xff0c 2017 全国统一高考如期而至 朋友圈里的各种高考热文如流水般 xff0c 不停歇地出现在眼前 xff0c 难免会勾起自己曾经的青涩时光 还记得 xff0c 考试前 xff0
  • STM32驱动ESP8266连接阿里云(2)----接入阿里IoT Studio实现Web可视化

    烧录MQTT固件 概述 阿里IoT Studio是一个物联网开发平台 xff0c 可用于快速构建基于云端的物联网应用 它提供了丰富的物联网组件和工具 xff0c 使得开发者可以轻松地进行设备接入 数据存储 数据分析等操作 要实现Web可视化
  • ‘gbk‘ codec can‘t encode character解决方法

    一 问题 xff1a 在将网络数据流导入文件时 xff0c 有可能遇到 39 gbk 39 codec can 39 t encode characte 错误 二 分析 xff1a 1 在windows下面 xff0c 新文件 xff08
  • ROS中的tf(transform)的理解 ,你追我小乌龟的深入剖析

    对于ros中的tf其实一直理解不是很深 xff0c 最近工作上一直在用 xff0c 就很懵逼 xff0c 出来混果然是要还的 xff5e 于是这两天把ros官方提供的小乌龟版的你追我 xff0c 如果你追到我 xff0c 我就让你xxx x
  • 线程同步之信号量(sem_init,sem_post,sem_wait)

    信号量和互斥锁 mutex 的区别 xff1a 互斥锁只允许一个线程进入临界区 xff0c 而信号量允许多个线程同时进入临界区 不多做解释 xff0c 要使用信号量同步 xff0c 需要包含头文件semaphore h 主要用到的函数 xf
  • 老程序员给的10条建议,句句经典

    1 想清楚 xff0c 再动手写代码 刚入行的新手 xff0c 为了展示自己的能力 xff0c 拿到需求迫不及待地就开始上手写代码 xff0c 大忌 xff01 2 不交流 xff0c 就会头破血流 不爱说话和沟通 xff0c 需求都理解错
  • Clickhouse快速上手 原理篇

    1 背景 公司目前使用Greenplum作为报表实时聚合查询的OLAP数据库 xff0c 当时主要是其使用门槛相对较低 xff0c 同时支持事务 xff0c 能在用户访问时候事务更新数据 xff0c 使用云厂商的产品 xff0c 技术支持也
  • Clickhouse快速上手 使用篇

    接着clickhouse原理篇 xff0c 下面来介绍他的具体使用场景 xff0c 包括数据导入 xff0c 更新等 文章目录 1 数据导入调研计划实施1 cos文件系统集成2 编码获取 2 数据更新和使用 1 数据导入 根据官方介绍 Cl
  • linux基本服务之sshd

    这段时间在学习linux常用服务 xff0c 这里将学习内容以及自己的实验心得记录下来 在自己忘记的时候也好复习 实验环境 xff1a centos 6 7 64bit 1 简介 SSHD服务 介绍 xff1a SSH协议 xff1a 安全
  • Git+VSCode基本使用

    前言 由于工作需要 xff0c 最近简单学习了git xff0c 巧合发现了VSCODE编辑器正好集成了git命令 xff0c 使得本地代码管理变得更加容易 因为最后是在linux下交叉编译 xff0c 但是我更习惯windows下写代码
  • 掌握音频开发基础知识

    文章目录 基本概念几种CODEC介绍实时调度相关缓冲区两种类型编写要点遇到的问题 解码能力的自适应混音模块回声消除的延时控制能量统计双声道支持ALSA设备 代码相关 基本概念 采样率 Hz 每秒去取样本的个数 xff0c eg 48000H