DirectX编程:利用 DirectSound 录音

2023-11-11


转载:http://www.cnblogs.com/stg609/archive/2008/10/24/1318931.html
      花了一阵子,把DirectX安装后自带的帮助文件中的那部分关于DirectSound录音这块给看完了,顺便把那部分翻译成了中文,有些地方可能翻译的不是很通顺,不过总体上还是能看得懂的。查看中文翻译,建议大家在进行学习前可以先去看看。
      期间也看了些别人的相关文章,感觉有点思路后就开始动手了,很高兴最后能顺利通过测试。不过我这个功能很简单,也不完善,只是最基本的可以录音。还待以后深入学习。

      开发平台:VS.NET 2005 ,Windows XP SP2 ,DirectX SDK(June 2008)下载页面 。
      必须的硬件设备:麦克风、声卡(集成或独立均可)、音响(能播放声音就行)

      首先,我们来温故下声卡和声音的基础知识。

      [摘自网络]  声音其实是一种能量波,因此也有频率和振幅的特征,频率对应于时间轴线,振幅对应于电平轴线。波是无限光滑的,弦线其实由无数点组成,由于存储空间是相对有限的,数字编码过程中,必须对弦线的点进行采样。采样的过程就是抽取某点的频率值,很显然,在一秒中内抽取得点越多,获取得频率信息更丰富,为了复原波形,一次振动中,必须有2个点的采样,人耳能够感觉到的最高频率为20kHz,因此要满足人耳的听觉要求,则需要至少每秒进行40k次采样,用40kHz表达,这个40kHz就是采样率。我们常见的CD,采样率为44.1kHz。光有频率信息是不够的,我们还必须获得该频率的能量值并量化,用于表示信号强度。量化值为2的整数次幂,我们常见的CD位16bit的采样大小,即2的16次方。


上图中A线条表示原始信号,而线条B和C表示不同采样率和采样大小的数字信号。采样率和采样大小的值越大,记录的波形更接近原始信号。


      采样频率一般分为22.05kHz、44.1kHz、48kHz三个等级,22.05kHz只能达到FM(调频)广播的声音品质,44.1kHz则是理论上的CD音质极限,48kHz则更加精确一些。对于高于48kHz的采样频率,人耳已无法听到,所以在电脑中也没有多少实用价值。
      目前的声卡基本都具有输入和输出信号的能力,这也是声卡具有最基本功能(录制声音、播放声音)的基础。如果可以同时输出和输入信号,这块声卡就得支持全双工的工作模式,这便是网络上进行语音通讯的基础。
      我们要讲的录音,就是将采集自麦克风的模拟信号转换成数字信号(ADC),一般麦克风只能提供模拟信号。
      一般声卡采集到的数据会被存放到缓存区后进行处理,如果是集成声卡,那就是先把数据放在内存中后再处理,你可以通过任务管理器来查看WINDOWS自带的录音机在录音时候内存的变化。
 

      对以上内容有所了解后,我们接着来了解下利用DirectSound录制声音的基本步骤。

       1 DirectSound是什么?
       DirectSound 是微软提供的DirectX API 的一部分。它使你能以极低的时延播放声音,使应用程序可以高度利用硬件资源。

       2 DirectSound能做什么?
       2.1 按照WAV格式播放声音。
       2.2 可以同时播放多种声音。
       2.3 将高优先级的声音分配给由硬件控制的缓冲区。
       2.4 将普通的声音融入自定的3D环境中。
       2.5 可以给声音添加不同的效果,比如回声,合唱等。
       2.6 从麦克风或其它音频输入设备中捕获WAV声音。

       3 DirectSound有哪些主要对象?

对象 说明 作用 .Net中的类或结构体
   设备对象   每个应用程序只有一个设备对象   用来管理设备,创建辅助缓冲区   Microsoft.DirectX.DirectSound.Capture
   主缓冲区   一个应用程序只有一个主缓冲区   操控声音捕捉缓冲区和产生混音效果的区域  Microsoft.DirectX.DirectSound.CaptureBuffer
   辅助缓冲区   每一个声音对应一个辅助缓冲区,可以有多个辅助缓冲区   用来存储要播放的声音文件,可建立多个辅助缓冲区来放多个要播放的声音文 Microsoft.DirectX.DirectSound.SecondaryBuffer
  事件通知对象   一个缓冲区可以有多个通知对象   用于在缓冲区的特定点触发通知事件,来通知程序执行操作   Microsoft.DirectX.DirectSound.Notify

       4 其它辅助对象

对象 作用 .Net中的类或结构体
   音频格式   定义WAV音频格式,如采样频率、量化位数、声道数等   Microsoft.DirectX.DirectSound.WaveFormat
   通知的事件   通知正在等待的线程已发生事件   System.Threading.AutoResetEvent


      5 必需知道的关键点
      实在是太不厚道了!!!!!辛苦写了半天,提交前没有事先复制一份,结果提交了半天给我展示了一个“无法显示该页面”,害得我又得从上次保存的地方开始写。真晕呀!!还不知道能不能记起刚才写的。都有点不想写了,可又觉得可惜。可恶!!可恶!!
      5.1 WAVE格式
      WAVE是录音时用的标准的WINDOWS文件格式,扩展名为“WAV”,
      我们使用DirectSound采集的WAV声音,其音频数据是按照PCM(脉冲编码调制,对连续变化的模拟信号进行抽样、量化和编码产生的数据,0和1的组合)调制后放入缓冲区的。
      WAVE文件格式采用RIFF文件格式结构,对PCM数据和其它一些音频信息进行相应的编排,从而最终形成的WAVE文件才能被音频播放器识别,才能进行播放。

       5.2 缓冲区指针
       缓冲区是存放音频数据的地方,并且它还提供了我们两个指针:读指针和捕捉指针。它们的位置按照相对于缓冲区起始位置的偏移量计算。读指针位于当前已经被完全捕捉到缓冲区的数据末尾。捕捉指针位于当前将要从硬件中复制的数据块的末尾。如果你想从缓冲区中读取数据,则只能从已经完全写入缓冲区的数据中读取,也就是说我们只能从偏移量小于读指针的地方读取。

       5.3 缓冲区通知
       大家应该都知道时间相同的音频文件,WAVE文件会比其它格式的音频文件大得多,这是因为WAVE文件没有对数据进行压缩。如果录音的时候,不限制缓冲区大小,那么你录制很短的时间可能就会占用很多内存,说不定不过多久,你的1G内存就不够用了。因此我们必须对缓冲区的大小进行限制,而且当缓冲区满了之后,还可以重新从缓冲区起始处开始,用新的数据覆盖旧的数据。那旧的数据怎么办呢?如果你不想丢失旧的数据,那就得在旧的数据被覆盖之前,将它转移到其它地方。
      如何才能在旧的数据没有被覆盖之前,将它转移走呢?如果是你,你会采用什么办法?
      有人提出通过轮询的办法,经常询问缓冲区是否满,满了则进行转移操作。可是这样做会相当耗费性能。微软提供了我们一个解决办法:“通知”。我们可以在缓冲区中的某些位置处设置通知,当读指针到达通知位置的时候,就会触发相应的事件执行转移操作。是不是有点像操作系统中的“响应中断”呢?

      6 录音大致过程
      6.1 设置PCM格式(很多人喜欢说是设置WAVE格式,但是个人觉得这样说并不恰当,因为PCM才是用来描述数据采集的,而WAVE只是一种文件格式。),设置相关的参数,如:采样频率、量化位数等。
      6.2 创建WAVE文件,有没有搞错呀?数据还没开始采集,怎么就先创建文件了呢?我可以很明确得告诉你没有错。因为RIFF结构的WAVE文件除了音频数据之外,还有其它数据,比如音频格式、格式长度等类似于文件头的数据。有了文件头后,接下来就只需要把接收到的数据添加在这个后面就好了。当然你一定要最后写的话,也不是不可以。
      6.3 建立设备对象,建立缓冲区对象。
      6.4 设置缓冲区通知,设置通知被触发后的事件。
      6.5 准备就绪后,就可以开始录音了。
      6.6 当通知被触发后,建立一个新的线程来处理数据转移的事件。(建立一个新的线程,就是为了防止录音过程被中断)。
      6.7 录音结束,写入WAV文件尾。这样一个可以播放的WAVE文件就OK了。


      具体代码
      不知道怎么形容现在的感觉了,真得很高兴大部分内容还记得。不知道你现在是何心情,是否已经没什么热情看下去了?本来想分两篇写的,但后来想想还是不浪费首面原创区的空间了。如果你前面的已经看懂了,那下面对你来说可能只是写写代码的事了。

      1.需要引用的命名空间和外部dll。

        两个外部DLL为:Microsoft.DirectX.dll 和 Microsoft.DirectX.DirectSound.dll

using  System.Threading;
using  System.IO;
using  Microsoft.DirectX.DirectSound;
using  Microsoft.DirectX;

       2.用户变量

        private string strRecSaveFile = string.Empty;//文件保存路径
        private Notify myNotify = null;//缓冲区提示事件
        private FileStream fsWav = null;//保存的文件流
        private int iNotifyNum = 16;//通知的个数
        private int iBufferOffset = 0;//本次数据起始点, 上一次数据的终点。
        private int iSampleSize = 0;//所采集到的数据大小
        private int iNotifySize = 0;//通知所在区域大小
        private int iBufferSize = 0;//缓冲区大小
        private BinaryWriter mWriter;
        
private Capture capture = null;//捕捉设备对象
        private CaptureBuffer capturebuffer = null;//捕捉缓冲区
        private AutoResetEvent notifyevent = null;
        
private Thread notifythread = null;
        
private WaveFormat mWavFormat;//PCM格式

       3.设置PCM格式
         private  WaveFormat SetWaveFormat()
        {
            WaveFormat format 
=   new  WaveFormat();
            format.FormatTag 
=  WaveFormatTag.Pcm; // 设置音频类型
            format.SamplesPerSecond  =   22050 ; // 采样率(单位:赫兹)典型值:11025、22050、44100Hz
            format.BitsPerSample  =   16 ; // 采样位数
            format.Channels  =   1 ; // 声道
            format.BlockAlign  =  ( short )(format.Channels  *  (format.BitsPerSample  /   8 )); // 单位采样点的字节数
            format.AverageBytesPerSecond  =  format.BlockAlign  *  format.SamplesPerSecond;
            
return  format;
            
// 按照以上采样规格,可知采样1秒钟的字节数为22050*2=55100B 约为 53K
        }

       4.创建WAVE文件  

        private void CreateWaveFile(string strFileName)
        {
            fsWav 
= new FileStream(strFileName, FileMode.CreateNew);
            mWriter 
= new BinaryWriter(fsWav);
            
/**************************************************************************
               Here is where the file will be created. A
               wave file is a RIFF file, which has chunks
               of data that describe what the file contains.
               A wave RIFF file is put together like this:
               The 12 byte RIFF chunk is constructed like this:
               Bytes 0 - 3 :  'R' 'I' 'F' 'F'
               Bytes 4 - 7 :  Length of file, minus the first 8 bytes of the RIFF description.
                                 (4 bytes for "WAVE" + 24 bytes for format chunk length +
                                 8 bytes for data chunk description + actual sample data size.)
                Bytes 8 - 11: 'W' 'A' 'V' 'E'
                The 24 byte FORMAT chunk is constructed like this:
                Bytes 0 - 3 : 'f' 'm' 't' ' '
                Bytes 4 - 7 : The format chunk length. This is always 16.
                Bytes 8 - 9 : File padding. Always 1.
                Bytes 10- 11: Number of channels. Either 1 for mono,  or 2 for stereo.
                Bytes 12- 15: Sample rate.
                Bytes 16- 19: Number of bytes per second.
                Bytes 20- 21: Bytes per sample. 1 for 8 bit mono, 2 for 8 bit stereo or
                                16 bit mono, 4 for 16 bit stereo.
                Bytes 22- 23: Number of bits per sample.
                The DATA chunk is constructed like this:
                Bytes 0 - 3 : 'd' 'a' 't' 'a'
                Bytes 4 - 7 : Length of data, in bytes.
                Bytes 8 -: Actual sample data.
              **************************************************************************
*/
            
char[] ChunkRiff = { 'R''I''F''F' };
            
char[] ChunkType = { 'W''A''V''E' };
            
char[] ChunkFmt = { 'f''m''t'' ' };
            
char[] ChunkData = { 'd''a''t''a' };
            
short shPad = 1;                // File padding
            int nFormatChunkLength = 0x10;  // Format chunk length.
            int nLength = 0;                // File length, minus first 8 bytes of RIFF description. This will be filled in later.
            short shBytesPerSample = 0;     // Bytes per sample.
            
// 一个样本点的字节数目
            if (8 == mWavFormat.BitsPerSample && 1 == mWavFormat.Channels)
                shBytesPerSample 
= 1;
            
else if ((8 == mWavFormat.BitsPerSample && 2 == mWavFormat.Channels) || (16 == mWavFormat.BitsPerSample && 1 == mWavFormat.Channels))
                shBytesPerSample 
= 2;
            
else if (16 == mWavFormat.BitsPerSample && 2 == mWavFormat.Channels)
                shBytesPerSample 
= 4;
            
// RIFF 块
            mWriter.Write(ChunkRiff);
            mWriter.Write(nLength);
            mWriter.Write(ChunkType);
            
// WAVE块
            mWriter.Write(ChunkFmt);
            mWriter.Write(nFormatChunkLength);
            mWriter.Write(shPad);
            mWriter.Write(mWavFormat.Channels);
            mWriter.Write(mWavFormat.SamplesPerSecond);
            mWriter.Write(mWavFormat.AverageBytesPerSecond);
            mWriter.Write(shBytesPerSample);
            mWriter.Write(mWavFormat.BitsPerSample);
            
// 数据块
            mWriter.Write(ChunkData);
            mWriter.Write((
int)0);   // The sample length will be written in later.
        }

       5.建立两个对象

        private bool CreateCaputerDevice()
        {
            
//首先要玫举可用的捕捉设备
            CaptureDevicesCollection capturedev = new CaptureDevicesCollection();
            Guid devguid;
            
if (capturedev.Count > 0)
            {
                devguid 
= capturedev[0].DriverGuid;
            }
            
else
            {
                MessageBox.Show(
"当前没有可用于音频捕捉的设备""系统提示");
                
return false;
            }
            
//利用设备GUID来建立一个捕捉设备对象
            capture = new Capture(devguid);
            
return true;
        }

        
private void CreateCaptureBuffer()
        {
//想要创建一个捕捉缓冲区必须要两个参数:缓冲区信息(描述这个缓冲区中的格式等),缓冲设备。

            CaptureBufferDescription bufferdescription 
= new CaptureBufferDescription();
            bufferdescription.Format 
= mWavFormat;//设置缓冲区要捕捉的数据格式
            iNotifySize = 1024;//设置通知大小
            iBufferSize = iNotifyNum * iNotifySize;
            bufferdescription.BufferBytes 
= iBufferSize;
            capturebuffer 
= new CaptureBuffer(bufferdescription, capture);//建立设备缓冲区对象
        }

       6.设置通知以及相应的事件

        //设置通知
        private void CreateNotification()
        {
            BufferPositionNotify[] bpn 
= new BufferPositionNotify[iNotifyNum];//设置缓冲区通知个数
//设置通知事件
            notifyevent = new AutoResetEvent(false);
            notifythread 
= new Thread(RecoData);
            notifythread.Start();
            
for (int i = 0; i < iNotifyNum; i++)
            {
                bpn[i].Offset 
= iNotifySize + i * iNotifySize-1;//设置具体每个的位置
                bpn[i].EventNotifyHandle = notifyevent.Handle;
            }
            myNotify 
= new Notify(capturebuffer);
            myNotify.SetNotificationPositions(bpn);
            
        }
        
//线程中的事件
        private void RecoData()
        {
            
while (true)
            {
                
// 等待缓冲区的通知消息
                notifyevent.WaitOne(Timeout.Infinite, true);
                
// 录制数据
                RecordCapturedData();
            }
        }

        
//真正转移数据的事件,其实就是把数据转移到WAV文件中。
        private void RecordCapturedData()
        {
            
byte[] capturedata = null;
            
int readpos = 0, capturepos = 0, locksize = 0;
            capturebuffer.GetCurrentPosition(
out capturepos, out readpos);
            locksize 
= readpos - iBufferOffset;//这个大小就是我们可以安全读取的大小
            if (locksize == 0)
            {
                
return;
            }
            
if (locksize < 0)
            {
//因为我们是循环的使用缓冲区,所以有一种情况下为负:当文以载读指针回到第一个通知点,而Ibuffeoffset还在最后一个通知处
                locksize += iBufferSize;
            }

            capturedata 
= (byte[])capturebuffer.Read(iBufferOffset, typeof(byte), LockFlag.FromWriteCursor, locksize);
            mWriter.Write(capturedata, 
0, capturedata.Length);//写入到文件
            iSampleSize += capturedata.Length;
            iBufferOffset 
+= capturedata.Length;
            iBufferOffset 
%= iBufferSize;//取模是因为缓冲区是循环的。
        }


       7.开始捕捉
       调用缓冲区的START方法就可以开始捕捉了。

       8.结束捕捉并写入WAV文件尾


        private void stoprec()
        {
            capturebuffer.Stop();
//调用缓冲区的停止方法。停止采集声音
            if (notifyevent != null)
                notifyevent.Set();
//关闭通知
            notifythread.Abort();//结束线程
            RecordCapturedData();//将缓冲区最后一部分数据写入到文件中

            
//写WAV文件尾
            mWriter.Seek(4, SeekOrigin.Begin);
            mWriter.Write((
int)(iSampleSize + 36));   // 写文件长度
            mWriter.Seek(40, SeekOrigin.Begin);
            mWriter.Write(iSampleSize);                
// 写数据长度
            mWriter.Close();
            fsWav.Close();
            mWriter 
= null;
            fsWav 
= null;

        }


       这样, 基本就完成了。但是并没有进行完善。这个还待日后改善。
      
       感叹呀~~写这篇,真得好不容易,感觉卡得就像个...。

       参考:http://blog.donews.com/uplook/archive/2005/12/14/657145.aspx
               http://www.cnblogs.com/onlytiancai/archive/2008/08/02/p2p_sound_chat.html



from:http://blog.csdn.net/tanqiuwei/article/details/44003211

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

DirectX编程:利用 DirectSound 录音 的相关文章

  • webrtcvad 安装失败

    倒腾了两个小时终于解决了这个问题 所有办法都试了 只有这个管用 下载安装VC 不要下载那种在线安装的 我试了很多次 都是安装包丢失或损坏 直接复制下面链接去下载 百度网盘链接 https pan baidu com s 1IaqkukMzb
  • 关于安卓上pcm文件转wav全是噪音解决办法

    1 一开始发现8bit的pcm能正常转换 但换成16bit转换出来全是噪音 网上资料也不全 思考了很久 突然想起大小端的问题 进行大小端处理后再进行转换 完美播放 下面贴出大小端转换方法 public class BigorLittle p
  • 10分钟上手Azure Blob Storage

    文章目录 Azure Blob Storage快速上手 背景 什么是Azure Blob Storage Blob Storage的应用场景 环境搭建 安装 运行 修改Blob Storage中的数据 基本操作 使用C 修改文件属性 遇到问
  • AMR文件格式的解释

    一 什么是AMR AMR WB 全称Adaptive Multi Rate和Adaptive Multi Rate Wideband 主要用于移动设备的音频 压缩比比较大 但相对其他的压缩格式质量比较差 由于多用于人声 通话 效果还是很不错
  • OGG流媒体文件格式分析

    摘自 http www studa net yingyong 080505 16283240 html 摘 要 流媒体文件格式在流媒体系统中占有重要地位 设计合理的文件格式是提高流媒体服务器工作效率最直接和最有效的办法 该文在剖析常用流媒体
  • AMR 文件解析及编解码流程

    CONTENT AMR简介 AMR 话音质量评定 AMR 文件结构解析 AMR 帧结构解析 AMR 帧读取算法 AMR 解码原理及流程 AMR 模式选择自适应机制 一 AMR 简介 基于新的网络和新的要求 无论是从节省传输频带资源 还是保持
  • python:pydub模块

    一 安装 1 安装模块 pip install pydub 2 安装插件 云盘中下载文件ffmpeg 打开电脑上的控制面板 系统 高级系统设置 环境变量 然后双击path 看到如下的界面 然后点新建会出现一个新建的地址栏 你需要在这个新建地
  • C++ 播放音频流(PCM裸流)

    直接上代码 如果有需要可以直接建一个win32控制台程序然后将代码拷过去改个文件名就可以用了 注意将声道和频率与你自己的文件对应 当然我自己也用VS2008写了个例子上传了 如果有需要下载地址如下 点击打开链接 这份代码是打开文件截取一段数
  • A²B汽车音频总线介绍

    A B使远程I S TDM成为可能 I S是飞利浦公司为数字音频设备之间的音频数据传输而制定的一种总线标准 该总线专责于设备之间的数据传输 广泛应用于各种多媒体系统 I C是两线式串行总线 用于连接微控制器及其外围设备 简单来说就是I C传
  • BES提示音修改实验

    加v hezkz17 进数字音频系统答疑群 1 普通提示音切换需要注意的 比如切换不同的音频信号源注意先要app audio list clear 然后再切换音频app audio manager sendrequest APP BT ST
  • 六、Audio-ALSA架构中的codec

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

    DirectX编程 利用 DirectSound 录音 转载 http www cnblogs com stg609 archive 2008 10 24 1318931 html 花了一阵子 把DirectX安装后自带的帮助文件中的那部分
  • 免费的包噪音网站分享

    免费的包噪音网站分享 现代生活中 噪音扰人 影响健康和情绪 白噪音可以为人们提供放松心情 提高睡眠质量和专注力的帮助 现在有很多免费的白噪音网站可以任意使用和分享 包括海浪声 雨声 蝉鸣声等等 非常适合在办公室 家里或者旅途中使用 本文为您
  • Linux车机平台pulseaudio多alsasink配置

    https www freedesktop org wiki Software PulseAudio 官网上的介绍是这样的 pulseaudio 是一个POSIX操作系统上的声音系统 是音频应用的代理 它允许你对音频数据 在从应用传递到硬件
  • FPGA微型板Verilog简单音频

    简单音调生成 该模块通过使用一个计数器生成一个1 kHz的信号 该计数器在CLK的每个刻度上都递增 当计数器达到32 000时 将切换输出BUZZER 并将计数器重置为0 音频输出 使用一个1 k 电阻器和一小段实心线将GPIO引脚P97和
  • pulseaudio使用过程中遇到的问题

    W pulseaudio main c This program is not intended to be run as root unless system is specified E pulseaudio core util c H
  • Android开发之合并文件的几种方式

    下面介绍合并文件的几种方式 并通过合并amr文件来举例介绍合并文件的具体流程 amr格式的文件头是6字节 所以在进行文件合并的时候要减去除第一个文件以外的其他文件的文件头 注意 不同文件的文件头是不一样的 所以在合并的时候根据不同文件相应的
  • 采样位数、采样率、波特率

    实例 16bit 16K 115200 1 采样位数 即采样值或取样值 就是将采样样本幅度量化 它是用来衡量声音波动变化的一个参数 也可以说是声卡的分辨率 它的数值越大 分辨率也就越高 所发出声音的能力越强 在计算机中采样位数一般有8位和1
  • 解决:soundfile打开opus文件出错: File contains data in an unimplemented format.

    Python的soundfile库依赖于libsndfile库 需要安装最新版本 sudo apt get update sudo apt get install libsndfile1 如果之前已经安装soundfile 则可能采用了旧版
  • 免费音效素材网站,一次性介绍清楚

    不管是在游戏 电影 电视剧 短视频还是音频中 合适的音效能够更好的表达内容和渲染氛围 今天给大家分享几个免费音效素材 感兴趣的话可以接着往下看 一 制片帮素材 找音效 制片帮素材不仅有海量的优质视频素材 还有丰富的音效资源 分类清晰 更重要

随机推荐

  • 原生js的e.target.closest()方法

    closest 方法 首先检查当前元素是否匹配 如果匹配则直接返回当前元素本身 如果不匹配则沿着dom树一层一层向上查找祖先元素 直到找到匹配的祖先元素为止 如果都不不匹配则返回空null 用法 比如 有一个ul列表 当点击ul里面的内容时
  • cmake can not determine linker.....

    目录结构如下 a hpp CMakeLists txt 内容如下 add library a a hpp 编译该目录 报上面的错误 cmake实际需要有 cpp的文件才能单独编译 只有hpp不行 但是如果加上链接库也是可以的 上面的内容加上
  • 嵌入式软件开发

  • vue element checkbox多选框 多组联动数据嵌套多选、单选实际应用

    先看我最近项目中要实现的功能 点击 干部管理组 员工管理组 与下属员工复选框实现联动 后台返回的数据结构大概是这样的 title 干部 rows id 1 name 张三 id 2 name 张四 title 员工 rows id 3 na
  • bean的生命周期分析(三)

    目录 二 全流程梳理 2 6 创建bean 2 6 1 createBean 2 6 2 resolveBeanClass 2 6 3 prepareMethodOverrides 2 6 4 resolveBeforeInstantiat
  • 【闲谈】对于华为提出的“端云协同”渲染模式的一些看法

    本帖的主要内容 是发表一些对于华为 端云协同 的顾虑 端云协同是好事 我们本地算力可以云化 本地近乎走瘦终端路线 但这就会引发问题 这将大大增加运营商的网络传输流量业务 所以宽带套餐应该会又有变革 那不得不说一些现实点的状况 一 家用宽带可
  • sqli-labs:less-28(过滤了union和select)

    div div
  • C++——#ifndef和#ifdef宏定义的使用及作用介绍

    建议结合以下博客理解 头文件重复引用 https blog csdn net shenlanzifa article details 21071443 ifndef和 ifdef都是一种宏定义判断 作用是防止多重定义 ifndef是if n
  • element-plus 表单验证

    表单验证是使用率比较高的 和之前element版本也有些差别
  • 科技风UI除了蓝色,还有什么配色选择?

    今年做了一年的科技风 看到蓝色也是甚是觉得审美疲劳 在同类产品中体现出差异性也比较困难 寻思着除了蓝色 就没有别的配色选择了吗 通过对科技类设计的搜集 总结了科技风产品的配色文章参考 尽管我今年对蓝色够够了 但还是要分析下它作为FUI首选配
  • windows下,unity项目突然无法打开的挽救办法

    情景 项目在某次关机后就再也无法通过unity正常打开了 换了机器和unity版本也依然不行 如果你没做存档想必这是个令人非常崩溃的事情 但还有一种挽救的办法 至少是一种可能挽救的办法 那就是使用其他平台的unity打开项目 unity有w
  • 【Python网络蜘蛛】基础 - 多线程和多进程的基本原理

    文章目录 多线程和多进程的基本原理 多线程的含义 并发和并行 Python中的多线程和多进程 多线程和多进程的基本原理 在编写爬虫程序的时候 为了提高爬取效率 我们可能会同时运行多个爬虫任务 其中同样涉及多进程和多线程 多线程的含义 先了解
  • 面试官:为什么MySQL的索引要使用B+树,而不是其它树?比如B树?

    点击上方 Java之间 选择 置顶或者星标 你关注的就是我关心的 来源 https dwz cn exC8JdQS 上一篇 InnoDB的一棵B 树可以存放多少行数据 答案 约2千万 为什么是这么多 因为这是可以算出来的 要搞清楚这个问题
  • pandas创建与保存(导入与导出)dataframe

    文章目录 一 创建Dataframe 1 创建空dataframe 2 从list 创建dataframe 把list当做一列 把list当做一行 3 从 dict key value 创建dataframe 4 从 CSV 创建dataf
  • 布局数据存储,中国电子云意在何为?

    数据存储市场的未来在哪里 答案毋庸置疑是 云端 著名咨询机构Wikibon曾经做过一项统计 将全球三大云服务商的数据存储营收与传统存储厂商的营收进行对比 发现云服务商的数据存储业务规模已然赶上传统存储厂商 这揭示出一个不可阻挡的趋势 即随着
  • 线性回归算法(二)-- 最优解与损失函数

    介绍 要理解最优解和损失函数 我们需要先弄明白什么是误差 以简单线性回归为例 如下图所示 青色数据样本为真实值 y y y 直线上同一 x x x位置的红色样本点为预测值
  • qt操作第三方软件

    QT控制第三方软件方法 背景需求 实现思路 获取句柄方法 QT通过获取的信息操作 例子 控件ID为0或者控件ID和操作句柄相同怎么办 得到窗体x y height width 模拟键盘鼠标操作 附录 键值对照表 背景需求 通过前辈们写的软体
  • Shell--基础--06--传递参数

    Shell 基础 06 传递参数 1 介绍 我们可以在执行 Shell 脚本时 向脚本传递参数 1 1 脚本内获取参数的格式 格式为 n n 代表一个数字 0 执行的文件名 1 为执行脚本的第一个参数 2 为执行脚本的第二个参数 以此类推
  • AI时代,重新理解阿里云

    如果说 在数字化时代 阿里云给外界的标签是基于算力 数据等要素的基建角色 那么 在如今的智能化时代 基于自身强大的云计算能力和长期以往的AI技术积累 它的这种底座底色显然再一次被夯实 彰显 作者 皮爷 出品 产业家 宜昌城东大道 左侧是中国
  • DirectX编程:利用 DirectSound 录音

    DirectX编程 利用 DirectSound 录音 转载 http www cnblogs com stg609 archive 2008 10 24 1318931 html 花了一阵子 把DirectX安装后自带的帮助文件中的那部分