【VS开发】【智能语音处理】Windows下麦克风语音采集

简介

   这是我很早以前的大学毕业设计,忽然间找到贴出来以纪念自己的纯真年代...但是因为CSDN不给面子所以导致短短的一篇文章贴了足足7次..他老提时说文章超过了64K,老大,拜托,那是算上了里面的图片大小吧...:-(

   本文简单介绍了声卡的工作原理 , 录音的原理以及数字音频的基本知识并且利用 Windows 提供的 Waveform Aduio APIs 以及 Multimedia File I/O APIs 实现一个 Windows 环境下的麦克风录音以及将录音文件保存成 .wav 文件的简单系统 .

 

关键字

  Waveform Aduio APIs, Multimedia File I/O APIs,waveInXXX,

mmioXXX, 麦克风 , 录音 , 波形文件 ,VC6++

 

  要深入的了解麦克风录音的实现我们必须了解声卡的工作原理 ,麦克风录音的原理以及了解相关的编程接口,下面我们将慢慢道来…

 

1. 声卡的工作原理

 

声卡的工作原理其实很简单 ,我们知道,麦克风和喇叭所用的都是模拟信号,而电脑所能处理的都是数字信号,两者不能混用,声卡的作用就是实现两者的转换。从结构上分,声卡可分为模数转换电路和数模转换电路两部分,模数转换电路负责将麦克风等声音输入设备采到的模拟声音信号转换为电脑能处理的数字信号,而数模转换电路负责将电脑使用的数字声音信号转换为喇叭等设备能使用的模拟信号,就这么简单 。
CSDN_Dev_Image_2004-3-31505260.gif 
上图就是一块典型的声卡 , Mic 插口 用于连接麦克风 , 通过它可以录制外界的声音

2. 数字音频基础知识

  麦克风录音的过程其实就是将模拟信号转化成数字信号的过程 , 其中涉及的一些概念如下 :

 1. 采样率 (Sampling Rate)

    采样率指声卡在一秒之中对声音 ( 波形 ) 作记录的次数 , 根据研究声音播出时的质量常常只能达到采样率的一半 , 因此必须采取双倍的采样率才能将声音标准重现 . 也就是只要采样率大于原始信号频率的两倍以上即可减低错误 , 达到和原始声音差不多的质量 . 人的听力大概是 20KHZ, 所以高品质的采样率应为其两倍以上 .

    当声音来源为音乐时 , 因为它所横跨的频率变化极为宽广 , 通常以 44.1KHZ 的频率为 CD 音乐采样率的标准 . 但是若以语言为主由于人说话的语音大概是 10KHZ, 因此加倍采样 , 只取 22KHZ 即可 , 采样率越高所记录下来的音质就越清晰 , 当然 , 越高的采样所记录下的文件就越大 .

  2. 采样位

    解析度决定了采样的音波是否能保持原来的形状 , 越接近原型则需解析度越高 , 若以 8 位来采样的话其能表达的组合种类是 2 的 8 次方 , 即 256, 表示用 8 位的采样大小能分辨出 256 个层次的声音 , 若用 16 位来采样 , 则能分辨的差异将高达 2 的 16 次方 , 为 65536, 其精度自然大为提高 .16 位 ,8 位采样的差别在于动态范围的宽窄 , 动态范围宽广 , 音量起伏的大小变化就能够更精细的被记录下来 , 如此一来不论是细微的声音或是强烈的动感震撼 , 都可以表现的淋漓尽致 , 而 CD 音质的采样规格正式 16 位采样的规格 .

  3. 量化误差 (Quantization error)

       在采样的过程中 , 不断连续变化的模拟信号要用数字化的数值来表示 , 这样的过程就会发生所谓的量化误差 (Quantization error). 所谓的量化误差指的是实际的信号的振幅 (smplitude) 和数字化之后所的数字之间的差异 . 如果用将数字信号还原成模拟信号的角度看 , 量化误差就是失真 (Distortion). 我们可以用增加采样大小的方式来降低量化误差 , 也就是更多的位 (bits) 来表示一个采样信号 , 这样可以提高精度 .

 4. 量化 (Quantization), 线性量化法 (Linear quantization) 和非线性量化法 (Nonlinear quantization)

       所谓的量化 (Quantization) 就是将模拟信号所代表的连续范围分成一段一段的区间 (Interval), 每一段区间我们定义一个数字化的值 . 区间的数目是跟采样大小有关 , 举例来说 , 有一种最简单的量化法称为 ” 线性量化法 ”(Linear quantization), 这种量化法采用等距离的间隔空间 , 架设一个讯号它的最大值是 5.0, 采样大小为 3 位 , 则每个量化区间就时 5.0/2^3, 也就是 0.625 单位 . 另外一种相反的量化方法就是 ” 非线性量化法 ”(Nonlinear quantization), 这种量化法采用不同的间隔空间 . 以 ” 对数量化法 ”(Logarithm quantization) 为例 . 低振幅范围的量化区间就比高振幅的范围的区间较为接近 , 用这种量化的法产生的结果就是在低振幅时我们会得到佳好的效果 . 通常如果使用同样的采样大小 , 非线性量化法会比线性量化法得到更好的声音品质 . 但是如果是要对声音做滤波 (filtered) 或一些运算的时候 , 使用线性量化法会比较容易处理 .

 5. 声音强度

         波形振幅的平方.两个声音强度上的差常以分贝 (db) 为单位来度量 ,计算公式如下:

    20*log(A1/A2) 分贝 , A1,A2 为两个声音的振幅 .

a. 如果采样大小为 8 位 ,则采样的动态范围为 20*log(256) 分贝 =48db;

b. 如果样本大小为 16 位 ,则采样动态范围为 20*log(65536) 大约是 96 分贝 ,接近了人听觉极限和痛苦极限,是再线音乐的理想范围, windows 同时支持8 位和 16 位的采样大小 .

 

 6. 音频编码方法

      目前已经发展了许多音频编码的方法用以减少存储量或是传输的时间 , 以下所列为两种较普遍的编码方法 :

   a.PCM(Pulse code modulation);

脉冲编码调制 ,即对波形按照固定周期频率采样。为了保证采样后数据质量,采样频率必须是样本声音最高频率的两倍,这就是 Nyquist 频率 .

   b.ADPCM(Adaptive delta pulse modulation).

3. RIFF 文件结构和 WAVE 文件格式 

   Windows 支持两种 RIFF(Resource Interchange File Format," 资源交互文件格式 ") 格式的音频文件: MIDI 的 RMID 文件和波形音频文件格式WAVE 文件,其中在计算机领域最常用的数字化声音文件格式是后者,它是微软专门为 Windows 系统定义的波形文件格式( Waveform Audio ),由于其扩展名为 "*.wav" ,因而该类文件也被称为 WAVE 文件。 为了突出重点,有的放矢,本文涉及到的声音文件所指的就是 WAVE 文件。常见的 WAVE 语音文件主要有两种,分别对应于单声道( 11.025KHz 采样率、 8Bit 的采样值)和双声道( 44.1KHz 采样率、 16Bit 的采样值)。这里的采样率是指声音信号在进行 " 模→数 " 转换过程中单位时间内采样的次数。采样值是指每一次采样周期内声音模拟信号的积分值。对于单声道声音文件,采样数据为八位的短整数(short int 00H-FFH );而对于双声道立体声声音文件,每次采样数据为一个 16 位的整数( int ),高八位和低八位分别代表左右两个声道。 WAVE 文件数据块包含以脉冲编码调制( PCM )格式表示的样本。在进行声音编程处理以前,首先让我们来了解一下 RIFF 文件和 WAVE 文件格式。 

   RIFF 文件结构可以看作是树状结构,其基本构成是称为 " 块 " ( Chunk )的单元,每个块有 " 标志符 " 、 " 数据大小 " 及 " 数据 " 所组成,块的结构如图 2 所示:

 

块的标志符( 4BYTES )

数据大小 ( 4BYTES )

数据

                                                      图 2 

  从上图可以看出,其中 " 标志符 " 为 4 个字符所组成的代码,如 "RIFF" , "LIST" 等,指定块的标志 ID ;数据大小用来指定块的数据域大小,它的尺寸也为 4 个字符;数据用来描述具体的声音信号,它可以由若干个子块构成,一般情况下块与块是平行的,不能相互嵌套,但是有两种类型的块可以嵌套子块,他们是 "RIFF" 或 "LIST" 标志的块,其中 RIFF 块的级别最高,它可以包括 LIST 块。另外, RIFF 块和 LIST 块与其他块不同, RIFF 块的数据总是以一个指定文件中数据存储格式的四个字符码(称为格式类型)开始,如 WAVE 文件有一个 "WAVE" 的格式类型。 LIST 块的数据总是以一个指定列表内容的 4 个字符码(称为列表类型)开始,例如扩展名为 ".AVI" 的视频文件就有一个 "strl" 的列表类型。 RIFF 和 LIST 的块结构如下:

RIFF/LIST 标志符

数据 1 大小

数据 1

格式 / 列表类型

数据

                                                图 3 

   WAVE 文件是非常简单的一种 RIFF 文件,它的格式类型为 "WAVE" 。 RIFF 块包含两个子块,这两个子块的 ID 分别是 "fmt" 和 "data", 其中 "fmt" 子块由结构 PCMWAVEFORMAT 所组成,其子块的大小就是 sizeofof(PCMWAVEFORMAT), 数据组成就是 PCMWAVEFORMAT 结构中的数据。 WAVE 文件的结构如下图 4 所示:

标志符( RIFF )

数据大小

格式类型( "WAVE" )

"fmt"

Sizeof(PCMWAVEFORMAT)

PCMWAVEFORMAT

"data"

声音数据大小

声音数据

                                                    图 4 

   PCMWAVEFORMAT 结构定义如下:

typedef struct
{
     WAVEFORMAT wf;         // 波形格式 , 前面已经提过了 ; 
     WORD wBitsPerSample;// WAVE 文件的采样大小; 
}PCMWAVEFORMAT
 "data"子块包含WAVE文件的数字化波形声音数据,其存放格式依赖于"fmt"子块中wFormatTag成员指定的格式种类,在多声道WAVE文件中,样本是交替出现的。如16bit的单声道WAVE文件和双声道WAVE文件的数据采样格式分别如图5所示:

  16位单声道:

采样一

采样二

……

低字节

高字节

低字节

高字节

……


   16 位双声道:

采样一 ……

左声道

右声道

……

低字节

高字节

低字节

高字节

……


                             图 5

 

4. 硬件抽象层 ( HAL, Hardware Abstraction Layer )

     HAL 是一个可加载的核心模块 (HAL.dll) ,它为运行在 Windows NT 架构 ( 包括 WindowsNT4.0,Windows2000,WindowsXP) 上 的硬件平台提供低级接口 , HAL 隐藏各种与硬件有关的细节 ,例如: I/O 接口 ,中断控制器,声卡…这样的话如果用户需要访问声卡硬件的话只能通过该声卡的驱动程序来实现,声卡驱动程序再调用 HAL 中的相应例程来实现 ,下图显示了 HAL ,声卡驱动程序, Waveform Audio APIs ,我们的麦克录音程序之间的关系:

5. Waveform Audio

     Waveform Audio APIs 是 Microsoft 提供给广大 Win32 程序员用来给自己的应用程序添加声音支持的一套强大的 API, 它提供的功能如下:

         1. 打开 / 关闭 / 查询声音设备 ;

        2. 播放波形文件 ;

        3. 设置播放速度 ;

         4. 播放进度控制 ;

         5. 录音 ;

         6. 得到当前的播放位置 ;

        7. 调节音量 .

 

下面简单介绍一下这套 API 提供的主要函数 :

  • 打开录音设备函数

            MMRESULT waveInOpen(

            LPHWAVEIN phwi,                 // 输入设备句柄

            UINT uDeviceID,                   // 输入设备 ID

            LPWAVEFORMATEX pwfx,       // 录音格式指针

            DWORD dwCallback,              // 处理 MM_WIM_*** 消息的回调函数或窗  

                                                       // 口句柄,线程 ID

            DWORD dwCallbackInstance, 

            DWORD fdwOpen                  // 处理消息方式的符号位

          );

 

  • 为录音设备准备缓存函数

              MMRESULT waveInPrepareHeader(  HWAVEIN hwi, 

                                                                  LPWAVEHDR pwh,

                                                                  UINT bwh ); 

 

  • 给输入设备增加一个缓存

              MMRESULT waveInAddBuffer(  HWAVEIN hwi,

                                                           LPWAVEHDR pwh,

                                                           UINT cbwh ); 

 

  • 开始录音

              MMRESULT waveInStart(  HWAVEIN hwi  ); 

 

  • 清除缓存

              MMRESULT waveInUnprepareHeader( HWAVEIN hwi,

                                                                     LPWAVEHDR pwh,

                                                                     UINT cbwh); 

 

  • 停止录音

              MMRESULT waveInReset( HWAVEIN hwi ); 

 

  • 关闭录音设备

              MMRESULT waveInClose( HWAVEIN hwi ); 

 

  • Wave_audio 数据格式

              typedef struct {

                   WORD  wFormatTag; // 数据格式,一般为 WAVE_FORMAT_PCM 即

                                                   // 脉冲编码

                   WORD  nChannels;     // 声道

                   DWORD nSamplesPerSec;   // 采样频率

                   DWORD nAvgBytesPerSec;  // 每秒数据量

                   WORD  nBlockAlign;

                   WORD  wBitsPerSample;     // 样本大小

                   WORD  cbSize;

            } WAVEFORMATEX; 

  • waveform-audio 缓存格式 

             typedef struct {

                   LPSTR  lpData;                    // 内存指针

                   DWORD  dwBufferLength;    // 长度

                   DWORD  dwBytesRecorded; // 已录音的字节长度

                   DWORD  dwUser;

                   DWORD  dwFlags;

                   DWORD  dwLoops;              // 循环次数

                   struct wavehdr_tag * lpNext;

                   DWORD  reserved;

             } WAVEHDR; 

 

  • 相关消息 

             MM_WIM_OPEN: 打开设备时消息,在此期间我们可以进行一些初始化工作

             MM_WIM_DATA: 当缓存已满或者停止录音时的消息,处理这个消息可以对

                                     缓存进行重新分配,实现不限长度录音

             MM_WIM_CLOSE :关闭录音设备时的消息。

 

 

5. Multimedia File I/O

       Multimedia File I/O APIs 是 Microsoft 提供的另外一套强大的针对媒体文件 I/O 的 API, 我们知道许多像 MediaPlay,RealOne 这样的多媒体程序对媒体文件的读写性能要求很高 , 它们几乎要求实时的将磁盘上的媒体文件以流的形式读入 , 但是对于一般的文件 I/O 形式如图 1:
CSDN_Dev_Image_2004-3-31514310.jpg
1.文件从磁盘上被读入操作系统的File I/O的buffer;

    2. 然后拷贝到应用程序自己的 buffer 中 ;

    3. 应用程序这时候才能读取文件内容 .

        上述的过程对于多媒体应用程序来说是低效的而且浪费宝贵的内存资源,如果文件和大的话势必还要采取分段读取等机制, Multimedia File I/O 采取了一种直接存取机制 ( 如图 2), 使得应用程序可以直接读取操作系统的 File I/O buffer, 大大提高了效率 . 后面我们会利用此套 API实现录音文件的存储 .
CSDN_Dev_Image_2004-3-31514312.jpg 
 

6. 麦克录音系统简介

   本文实现的麦克录音系统将具备以下功能 :

      1. 录制用户通过麦克风发出的声音 ;

                这将利用到 Waveform APIs, 流程如下 :

                     a. 打开录音设备 waveInOpen;

                     b. 准备 wave 数据头 waveInPrepareHeader;

                     c. 准备数据块 waveInAddBuffer;

                     d. 开始录音 waveInStart;

                     e. 停止录音 (waveInReset);

                     f. 关闭录音设备 (waveInClose);

                     g. 当开始录音后当 buffer 已满时 , 将收到 MM_WIM_DATA 消息 , 处理该

                        消息可以保存已录好数据 .

 

       2. 根据用户的声音强弱动态显示声音波形 ;

            这主要通过 GDI 函数来实现 .

  

      3. 将用户通过麦克风发出的声音录制成 wav 文件保存 .

            这将利用到 Multimedia file I/O APIs.

                 a .调用 mminoOpen 函数来打开 WAVE 文件 , 获取 HMMIO 类型的文件句柄 ; 
              b .根据 WAVE 文件的结构 , 调用 mmioRead 、 mmioWrite 和 mmioSeek 函数实现文件的读、写和定位操作 ; 
               c .调用 mmioClose 函数来关闭 WAVE 文件 .  

 

7. 麦克录音系统的实现 (MicDemo)   

 

下面是该系统的界面 :
CSDN_Dev_Image_2004-3-31517320.jpg 

 对于录音来说最重要的就是CSoundIn类,下面就是该类的定义:

  namespace  perdubug   {   //  prevent the name-space pollution  
 

 class  CSoundIn  

 {

 public :    

                BOOL     __initMic();  //  get the best wave format supported by your sound card

                                                    
 //  and then i will use the format to capture sound. 
 

 

                 void      __closeMic();

 

                

                BOOL     __openMic();  //  open device and begin to capture with the best format(when 

                                                       
 //  invoke __initMic function then you will get the best format

                                                       
 //  supported by host's sound card

                

                
 // 
 
                
 //  if your want to capture sound and export into a wav file please invoke this function

                
 //  to tell me the full path then i will create the wav file.

                
 //
 
                 void      __createOutputWaveFile( const  TCHAR  *  lpszFileName);

                

                 //  if you invoke any member function return error/false please

                
 //  use this function to get the result 
 

                DWORD    __getLastError();

 

                 // 
 
                
 //  when the capture buffer is filled please invoke this function to 'add buffer'(Actually

                
 //  you should create two-circular buffers,when 1st buffer is filled then switch to 2st,1st

                
 //  buffer will be wrote into wav file.

                
 //
 
                 void  AddBuffer();

                

                 virtual   ~ CSoundIn();

                

                friend CSoundIn  &  theSoundCapture();

 private :

 

                BOOL  GetBestWaveFormat(WAVEFORMATEX  &  waveFormatEx);

                

                 //  because sound card is one and only so i must limit the number of CSoundIn object,

                
 //  but how to limit the class object nums?maybe put constructor into private scope is

                
 //  a good idea,:-) 
 

                CSoundIn();          

 

 private :

                WAVEINCAPS                    m_WaveInDevCaps;

                HWAVEIN                            m_WaveIn;

                WAVEHDR                           m_WaveHeader;

                WAVEFORMATEX            m_WaveFormat;  

                

                UINT    m_WaveInSampleRate;

                 int          m_NbMaxSamples;

                UINT    m_SizeRecord;

 

                DWORD   m_dwLastError;

 

                 enum   { MAX_SIZE_INPUT_BUFFER  =   1   *   2   *   1024  } ;  //  samples * voie * size_samples 
 

 

 public :

                SHORT  InputBuffer[MAX_SIZE_INPUT_BUFFER];    //  used for int FFT,many GUI application 

                                                                                                          
 //  want to display sound peak so.. 
 

                BOOL   m_bTerminateThread;                    //   to 'kill' waveCallback function 
 

                BOOL   m_bImportToWaveFile;

                

                CWaveFile       m_waveFile;

;

 

   //   end namespace perdubug 
 

 

对于将录音保存在WAV文件的工作主要是由CwaveFile类来完成.下面是该类的定义:

  // 
 
 
//   Encapsulates reading or writing sound data to or from a wave file

  //  ----------------------------------------------------------------------------- 
 

  class  CWaveFile

  {

 public :

    WAVEFORMATEX *  m_pwfx;         //  Pointer to WAVEFORMATEX structure 
 

    HMMIO              m_hmmio;               //  MM I/O handle for the WAVE 
 

    MMCKINFO      m_ck;                       //  Multimedia RIFF chunk 
 

    MMCKINFO      m_ckRiff;                //  Use in opening a WAVE file 
 

    DWORD             m_dwSize;              //  The size of the wave file 
 

    MMIOINFO       m_mmioinfoOut;

    DWORD             m_dwFlags;

    BOOL                 m_bIsReadingFromMemory;

    BYTE *                m_pbData;

    BYTE *                m_pbDataCur;

    ULONG              m_ulDataSize;

    CHAR *               m_pResourceBuffer;    

 protected :

    HRESULT ReadMMIO();

    HRESULT WriteMMIO( WAVEFORMATEX  * pwfxDest );

 

 public :

    CWaveFile();

     ~ CWaveFile();

 

    HRESULT Open( LPCTSTR strFileName, WAVEFORMATEX *  pwfx, DWORD dwFlags );

    HRESULT OpenFromMemory( BYTE *  pbData, ULONG ulDataSize, 

                                                        WAVEFORMATEX *  pwfx, DWORD dwFlags );

    HRESULT Close();

 

    HRESULT Read( BYTE *  pBuffer, DWORD dwSizeToRead, DWORD *  pdwSizeRead );

    HRESULT Write( UINT nSizeToWrite, BYTE *  pbData, UINT *  pnSizeWrote );

 

    DWORD   GetSize();

    HRESULT ResetFile();

    WAVEFORMATEX *  GetFormat()  {  return  m_pwfx; } ;

;

 
 我们有了这两个强有力的类的支持就可以开始我们的编程工作了….

    1 .用VC6 ++ 建立一个MFC基于对话框的工程:MicDemo;

    2 .添加我们的两个类CSoundIn,CwaveFile;        

    3 .当我们点击开始(Start)按钮的时候我们就要开始录音了…

 

           void  CMicDemoDlg::OnStart() 

          {

                m_btnStart.EnableWindow(FALSE);                

                 if (theSoundCapture().__initMic())

                 {

                      m_filePath.SetWindowText(_T( " yangchen.wav. " ));         

                      theSoundCapture().__createOutputWaveFile(_T( " yangchen.wav " ));                     

                      if ( ! theSoundCapture().__openMic())

                       {

                        ::MessageBox( this -> m_hWnd,_T( " Can not open microphone! " ), _T( " Error " ),MB_OK | MB_ICONERROR);

                          return ;

                      } 

 
                } 

 
                

                m_btnStop.EnableWindow(TRUE);

              

               //  设置定时器是为了画波形用的       
 

                 SetTimer( 1 ,  200   /* start slow */ , NULL);

           } 

 
  

     4 .在定时器的回调函数中画波形.

             void  CMicDemoDlg::OnTimer(UINT nIDEvent) 

              {

                     if (nIDEvent  ==   1 )

                    {           

                                 static   const   int  xCon  =   13 ;

                                 static   const   int  yCon  =   13 ;

                                 static   const   int  wCon  =   623 ;

                                 static   const   int  hCon  =   80 ;

                                

                                CClientDC dc( this );

                                

                                CBitmap   Bitmap;

                                CBitmap  *  pbmOld  =  NULL;                              

                                CDC         dcMem;

                                

                                dcMem.CreateCompatibleDC( & dc);                 

                                Bitmap.CreateCompatibleBitmap( & dc,wCon,hCon);

                                

                                pbmOld  =  dcMem.SelectObject( & Bitmap);

                                

                                dcMem.PatBlt( 0 , 0 ,wCon,hCon, WHITENESS);                              

                                dcMem.MoveTo( 0 ,hCon / 2 );

                                

                                 // 
 
                                
 //  display incomming signal--key idea!

                                
 //
 
                                 for ( int  x  = 0  ; x  <  wCon; x ++ )   //  display Input 
 

                                  {

                                          dcMem.LineTo(x,(hCon  >>   1 )  -  (theSoundCapture().InputBuffer[x]  >>   7 ));

                                } 

 
                                

                                dc.BitBlt(xCon,yCon,wCon,hCon, & dcMem,  0 ,  0 , SRCCOPY);

                                

                                dcMem.SelectObject(pbmOld);

                                dcMem.DeleteDC();             

                  } 

 
                   else 
 
                         CDialog::OnTimer(nIDEvent);

            } 

 
       

       5 .点击停止(Stop)按钮的时候停止录音和写WAV文件

                    void  CMicDemoDlg::OnStop() 

                   {

                        m_btnStop.EnableWindow(FALSE); 

                        theSoundCapture().__closeMic(); 

                        m_btnStart.EnableWindow(TRUE);

                 } 

 
看完整段代码你可能会很奇怪怎么在CmicDemoDlg中居然都没有定义一个CSoundIn对象 ?? 呵呵,原因很简单,因为设备的独占性所以在一个时刻只能有一个CSoundIn对象存在(因为CSoundIn对象需要占据录音设备),所以我们必须限制程序员生成CSoundIn对象的数量,怎么限制呢 ? 那就是把CSoundIn的构造函数放在private区域里面:

   

       private :

                BOOL  GetBestWaveFormat(WAVEFORMATEX  &  waveFormatEx);

                

                  //   because sound card is one and only so i must limit the number of CSoundIn object,

                
  //   but how to limit the class object nums?maybe put constructor into private scope is

                
  //   a good idea,:-) 
 

        CSoundIn();          

 

这样的话就根本无法声明一个CSoundIn对象,不信你试一下在你的代码中写上:

     CSoundIn  soundInObj;

 能编译通过吗 ? 肯定是不能,那如何调用CSoundIn的成员函数呢 ? 答案是通过一个全局函数:

 

                //   global function,:-(

              
  //   client can only through this function to use CSoundIn object 
 

              CSoundIn  &  theSoundCapture()

                {

                      static  CSoundIn p;

                      return  p;

               } 

 
 

  这时候你应该明白了为什么上面的代码中调用CSoundIn的成员函数的时候都是用theSoundCapture来做的原因了吧. 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【实时语音转文本】PC端实时语音转文本(麦克风外音系统内部音源)

语音转文字这个功能可以应用在视频动态字幕&#xff0c;语音快速输入&#xff0c;实时记录通话内容&#xff0c;高级应用可以在人工智能&#xff0c;语音识别&#xff0c;智能助手方面&#xff0c;还需要一点机器学习可以做出一些好玩的东西&#xff0c;比如PC端AI助理&#xf…

使用微软的语音识别引擎Microsoft Speech API进行语音控制

以下代码来自&#xff1a;http://mmcheng.net/zh/imagespirit/ 本人仅作提取&#xff1a; SREngine语音识别引擎封装类&#xff1a; #pragma once/************************************************************************/ /* Notice: this project is used to support sp…

微软语音合成(tts)服务申请和调用

1、申请账户&#xff1a; https://azure.microsoft.com/zh-cn/free/ 这里有个视频教程&#xff0c;根据此完成申请流程&#xff1a; https://www.bilibili.com/video/BV15a4y1W7re?vd_sourcebf07f28d37849885d215dc3aea189eba 申请完成后&#xff0c;就可以到这里申请资源&am…

英超引入 AI 球探,寻找下一个足球巨星

By 超神经 内容提要&#xff1a;球探对于很多人来说是一个略显神秘的群体&#xff0c;他们对一个球队的建设和发展至关重要。为了提高球探的工作效率&#xff0c;英超伯恩利足球俱乐部最近启用了 AI 球探。 关键词&#xff1a;AI 球探 计算机视觉 姿态识别 最近&#xff0c;中国…

通过whoscored网站对转会球员的评分来比较五大联赛水平高低

我是一个八年级的学生&#xff0c;上学期学习了Python和包括标准差在内的数据统计方法。参考“怕砍不留名”在soccer.hupu.com上的帖子&#xff0c;并且在爸爸的指导下我尝试对欧洲五大足球联赛&#xff08;英超、德甲、西甲、意甲、法甲&#xff09;水平高低做一个比较。 五大…

看卡塔尔世界杯,diff一下足球比赛与软件开发

diff一下足球比赛与软件开发 吐槽世界杯E组&#xff01;类比软件开发与足球比赛教练与架构师的作用 新技术——半自动越位技术世界杯冠军 吐槽世界杯E组&#xff01; 最近博主看了多场世界杯比赛&#xff0c;看的我心力交瘁&#xff0c;欲哭无泪。 从日本与哥斯达黎加那场比赛开…

一个值得关注的技术公众号「腾讯云开发者」

今天向大家推荐的是腾讯云技术社区的公众号&#xff0c;是腾讯官方推出的技术知识分享阵地&#xff0c;每周公开腾讯最新的技术工程方案&#xff08;如微信、健康码、王者荣耀等知名业务的技术架构&#xff09;、ChatGPT 等行业前沿热点解读、研发经验和工作方法等干货&#xf…

踩坑小总结

本来想总结一下自己工作到现在踩过的坑&#xff0c;实在是太多了&#xff0c;自己之前也年轻不懂事&#xff0c;没有记录的习惯&#xff0c;想到多少写多少吧。 1.前、后端分离框架&#xff1a;刚进入工作的时候采用的是easyuissh的框架&#xff0c;前后端一体&#xff0c;后来…

如何调教一个定制化的ChatGPT私人助理,接入自家知识库

大家好&#xff0c;欢迎来到 Crossin的编程教室 &#xff01; 我在之前的文章里介绍过&#xff0c;如何利用 OpenAI 开放的 API&#xff0c;将 ChatGPT 接入自己开发的程序&#xff1a; 把 ChatGPT 加到你自己的程序里 当时开放的模型还是 text-davinci-003。 文章发布后没多久…

ROS (roslaunch turtle_tf turtle_tf_demo.launch)报错-已解决(利用CHAT-GPT修bug)

在使用古月居的ROS入门21讲时&#xff0c;使用turtle_tf包学习tf功能时出现报错&#xff0c;网上的报错都是由于python版本的问题&#xff0c;修改后而无法解决。最后利用CHAT-GPT的修bug提示&#xff0c;最后发现是ros缓存导致报错。在文章的最后还会提供修改python版本的链接…

chatgpt赋能python:Python代码扫描:如何编写高质量的代码

Python 代码扫描&#xff1a;如何编写高质量的代码 如果你是一位有着10年的 Python 编程经验的工程师&#xff0c;你一定知道写出高质量的 Python 代码是多么的重要。但是&#xff0c;即使你是一名经验丰富的 Python 开发者&#xff0c;你的代码仍然可能存在一些隐藏的漏洞和低…

福成股份很忙:实控人李福成被罚8万,新董事长李良则要打官司

最近&#xff0c;福成股份&#xff08;SH:600965&#xff09;比较忙。 7月6日&#xff0c;河北福成五丰食品股份有限公司&#xff08;下称“福成股份”&#xff09;发布公告称&#xff0c;其近日收到证监会河北监管局&#xff08;下称“河北证监局”&#xff09;的《行政处罚决…

数据结构实验代码 1.单链表;2树的递归遍历

1.单链表 1.1算法描述 1.2代码 #include <stdio.h> #include<malloc.h> typedef char elemtype; typedef struct lnode { elemtype data; struct lnode *next; }linklist; void initlist (linklist *&L)//创建一个表 { L(linklist *)malloc(sizeof(linkli…

数据结构实验课考试 1.单链表;2树的递归遍历

1.单链表 1.1算法描述 1.2代码 1.3代码运行结果 2.树的递归遍历 2.1算法描述 2.2代码 2.3代码运行结果

vue+uview实现仿微信通讯录(提醒谁看功能)

实现功能如下图&#xff1a;(索引列表加头像和选择框&#xff0c;选择好的好友在顶部展示出来) 首先引入uview里面的索引列表组件&#xff1a;(用于展示好友列表) <view class"" style"width: 700rpx;margin: 0 auto;"> <u-index-lis…

day03_顺丰快递分拣小程序

目录 一、又见str1.1 字符串定义1.2 字符串常用操作1.2.1 str.center(width[, fillchar])1.2.2 str.count(sub, start 0,endlen(string))1.2.3 str.encode(encodingUTF-8,errorsstrict)1.2.4 str.endswith(suffix[, start[, end]])1.2.5 str.find(str, beg0, endlen(string))1.…

PYTHON 顺丰快递分拣小程序-极简9行代码实现分拣

给定一组打乱的快递信息数据&#xff0c;将快递按省份进行分类&#xff0c;最终实现如下格式效果&#xff1a; 数据源如下&#xff1a; [[王*龙, 北京市海淀区苏州街大恒科技大厦南座4层],[庞*飞, 北京市昌平区汇德商厦四楼403],[顾*锐, 江苏省扬州市三垛镇工业集中区扬州市立…

python快递分拣小程

需求 &#xff1a; 分拣出同一省份的信息 格式 省份 : 名字 地址 ...... 完整 代码 noe_list [[王*龙, 北京市海淀区苏州街大恒科技大厦南座4层],[庞*飞, 北京市昌平区汇德商厦四楼403],[顾*锐, 江苏省扬州市三垛镇工业集中区扬州市立华畜禽有限公司],[王*飞, 上海市徐汇区上…

重磅!2020国家科学技术奖全名单公布!

来源&#xff1a;软科 2021年11月3日&#xff0c;2020年度国家科学技术奖励大会在人民大会堂正式召开。 备受关注的国家最高科学技术奖&#xff0c;授予了中国航空工业集团有限公司顾诵芬院士和清华大学王大中院士。国家最高科学技术奖自2000年正式设立&#xff0c;是中国科技…

谷歌又闹大乌龙!Jeff Dean参与的新模型竟搞错Hinton生日

【导读】最近&#xff0c;谷歌研究员发布了关于指令微调的最新工作&#xff01;然而却宣传图中出现了可笑的乌龙。 几个小时之前&#xff0c;谷歌大脑的研究员们非常开心地晒出了自己最新的研究成果&#xff1a; 「我们新开源的语言模型Flan-T5&#xff0c;在对1,800多种语言…