首页 Windows环境下的麦克风录音系统

Windows环境下的麦克风录音系统

举报
开通vip

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

Windows环境下的麦克风录音系统
WindowsWindowsWindowsWindows环境下的麦克风录音系统环境下的麦克风录音系统环境下的麦克风录音系统环境下的麦克风录音系统 摘要摘要摘要摘要 本文简单介绍了声卡的工作原理 ,录音的原理以及数字音频的基本知识并且利用 Windows提供的WaveformAduioAPIs以及Multimedia File I/O APIs实现一个Windows环境 下的麦克风录音以及将录音文件保存成.wav文件的简单系统。 关键字:关键字:关键字:关键字:Waveform AduioAPIs, Multimedia File I/O APIs,waveInXXX,mmioXXX,麦克风,录音, 波形文件,VC6++ 要深入的了解麦克风录音的实现我们必须了解声卡的工作原理,麦克风录音的原理以及 了解相关的编程接口,下面我们将慢慢道来… 1.声卡的工作原理 声卡的工作原理其实很简单,我们知道,麦克风和喇叭所用的都是模拟信号,而电脑所能 处理的都是数字信号,两者不能混用,声卡的作用就是实现两者的转换。从结构上分,声卡可分 为模数转换电路和数模转换电路两部分,模数转换电路负责将麦克风等声音输入设备采到的 模拟声音信号转换为电脑能处理的数字信号,而数模转换电路负责将电脑使用的数字声音信 号转换为喇叭等设备能使用的模拟信号,就这么简单。 图1 上图就是一块典型的声卡,Mic插口用于连接麦克风,通过它可以录制外界的声音。 2.数字音频基础知识 麦克风录音的过程其实就是将模拟信号转化成数字信号的过程,其中涉及的一些概念如 下: 1.采样率(Sampling Rate) 采样率指声卡在一秒之中对声音(波形)作记录的次数,根据研究声音播出时的质量常常 只能达到采样率的一半,因此必须采取双倍的采样率才能将声音 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 重现.也就是只要采样率 大于原始信号频率的两倍以上即可减低错误,达到和原始声音差不多的质量.人的听力大概是 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,HardwareAbstraction Layer) HAL 是一个可加载的核心模块 (HAL.dll),它为运行在 Windows NT 架构 (包括 WindowsNT4.0,Windows2000,WindowsXP)上的硬件平台提供低级接口,HAL隐藏各种与硬件 有关的细节,例如:I/O接口,中断控制器,声卡…这样的话如果用户需要访问声卡硬件的话只能 通过该声卡的驱动程序来实现,声卡驱动程序再调用 HAL中的相应例程来实现,下图显示了 HAL,声卡驱动程序,WaveformAudio APIs,我们的麦克录音程序之间的关系: 图 6 5.5.5.5.WaveformWaveformWaveformWaveformAudioAudioAudioAudio WaveformAudio APIs是Microsoft提供给广大Win32程序员用来给自己的应用程序添 加声音支持的一套强大的 API,它提供的功能如下: 1.打开/关闭/查询声音设备; 2.播放波形文件; 3.设置播放速度; 4.播放进度控制; 5.录音; 6.得到当前的播放位置; 7.调节音量. 下面简单介绍一下这套 API提供的主要函数: • 打开录音设备函数 MMRESULTwaveInOpen( 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.5.5.5. MultimediaMultimediaMultimediaMultimedia FileFileFileFile I/OI/OI/OI/O Multimedia File I/O APIs 是 Microsoft提供的另外一套强大的针对媒体文件 I/O 的 API,我们知道许多像MediaPlay,RealOne这样的多媒体程序对媒体文件的读写性能 要求 对教师党员的评价套管和固井爆破片与爆破装置仓库管理基本要求三甲医院都需要复审吗 很高, 它们几乎要求实时的将磁盘上的媒体文件以流的形式读入,但是对于一般的文件 I/O形式如 图1: 图7 1.文件从磁盘上被读入操作系统的 File I/O的 buffer; 2.然后拷贝到应用程序自己的 buffer中; 3.应用程序这时候才能读取文件内容. 上述的过程对于多媒体应用程序来说是低效的而且浪费宝贵的内存资源,如果文件 和大的话势必还要采取分段读取等机制,Multimedia File I/O采取了一种直接存取机制(如图 2),使得应用程序可以直接读取操作系统的File I/O buffer,大大提高了效率.后面我们会利用此 套 API实现录音文件的存储. 图2222 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) 下面是该系统的界面: 对于录音来说最重要的就是 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(); HRESULTWriteMMIO( 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 ); HRESULTWrite( 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 &n bsp; 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的成员函数呢? 答案 八年级地理上册填图题岩土工程勘察试题省略号的作用及举例应急救援安全知识车间5s试题及答案 是通过一个全局函 数: // global function,:-( // client can only through this function to use CSoundIn object CSoundIn & theSoundCapture() { static CSoundIn p; return p; } 这时候你应该明白了为什么上面的代码中调用 CSoundIn 的成员函数的时候都是用 theSoundCapture来做的原因了吧.
本文档为【Windows环境下的麦克风录音系统】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_277611
暂无简介~
格式:pdf
大小:320KB
软件:PDF阅读器
页数:12
分类:互联网
上传时间:2011-07-14
浏览量:20