|
|
|
作者:任雪景、文娟
, X1 `- {4 [/ v8 L# M7 t! Z' t5 v 下载源代码7 J. G. ^6 o$ i
7 f% o- x m/ K) U# S; p 引言
/ ?' _2 o1 j/ Z- Z 我是C++的初学者,入门都要靠VCKBASE,好在里面有很多适合于初学者的例子,让我少走了很多弯路,为了回馈大家,我也把我最近刚完成的一个简单的小程序提供给大家,让那些曾经和我一样徘徊在C++大门之外的人能快些掌握要领,大家不妨看一看。- S; l1 n f, j% G+ B
本文以VC知识库第26期 栾义明 先生的《基于API的录音机程序》为基础的,在此深表感谢!相同之处不再重复,我在此基础上增加了将录音保存为wav文件的格式,便于大家参考。
% V* c! v* {7 s7 N基本步骤及思想:设定音频采集参数(采样率、声道等),打开音频设备、准备wave数据头和开辟缓存,操作采集的数据并保存为wav文件。设定音频回放参数,打开回放设备、准备wave数据头和写wave数据。另外样本程序需包含#include <mmsystem.h>和#pragmacomment(lib,"WINMM.LIB")多媒体支持。
9 _! P- X% V6 H; C, c4 d& I& w 在介绍程序前,需要你对wave文件的格式和相关一些基础概念有所了解,这些均可以在msdn中查找,为方便理解,我们将其整理,如果对这些基础知识已有所了解,可以跳过。
* j! Y" c+ k& _, n2 Z
* y7 W7 i) c A- ? 概念1、定义波形数据格式$ e9 H, ^# U8 q/ e1 k0 Y* o# g
typedef struct{WORD wFormatTag; / O3 {9 D D* E L
WORD nChannels; # ~+ M( |+ d% D8 t3 c# |2 x
DWORD nSamplesPerSec; $ t. k( _3 U J$ e9 \/ W) _
DWORD nAvgBytesPerSec;
. r5 m% C# k5 \% j2 s# s WORD nBlockAlign;
/ Z( D; Z! _0 _( u$ `1 h( h6 D7 @ WORD wBitsPerSample; 4 |# [- c [& ]1 u& n
WORD cbSize; } WAVEFORMATEX; 具体参数解释如下:+ q7 r" q3 \3 X0 v) P
wFormatTag:波形数据的格式,定义在MMREG.H文件中
~ B6 v# o8 |5 r" K7 w nChannels:波形数据的通道数:单声道或立体声
$ m, D* v+ z3 D9 i# v% h nSamplesPerSec:采样率,对于PCM格式的波形数据,采样率有8.0 kHz,11.025kHz,22.05 kHz,44.1 kHz等" P9 u8 A" u% G4 @7 D
nAvgBytesPerSec:数据率,对于PCM格式的波形数据,数据率等于采样率乘以每样点字节数$ Y/ ~0 a4 H/ q+ {+ d. x/ @, y
nBlockAlign:每个样点字节数
& H; r5 W+ R% u6 G5 h4 k- @" r5 s wBitsPerSample:采样精度,对于PCM格式的波形数据,采样精度为8或16, {0 J; K$ r& u/ p2 S9 B
cbSize:附加格式信息的数据块大小5 ^ |- A2 ]8 w" T. j- o4 Y
概念2、定义设备头结构5 u0 ^7 ?3 ^2 N9 x8 j
WAVEHDR定义了指向波形数据缓冲区的设备头。typedef struct { LPSTR lpData; 9 h y/ | W; `+ E* m; g7 P2 d8 k
DWORD dwBufferLength; h- s4 M" i5 J, z6 ^: E
DWORD dwBytesRecorded; : S1 S8 Y( g' f) t% c& }
DWORD dwUser;
4 Z, \9 A; H% y, |. n8 W$ `3 \- ^ DWORD dwFlags;
' U0 o* v, `/ {- }; }/ d0 ] DWORD dwLoops;
9 c0 O" W4 ~" t; Q- r1 u2 x, I struct wavehdr_tag * lpNext; 4 V% j/ U% B0 `8 k; j9 {
DWORD reserved; } WAVEHDR; lpData:波形数据的缓冲区地址
7 ?& L1 G w' b2 R# `4 J5 y dwBufferLength:波形数据的缓冲区地址的长度
3 C7 k. G- u) o7 C8 w2 | dwBytesRecorded:当设备用于录音时,标志已经录入的数据长度
6 N* V! d n; H+ I$ p* B dwUser:用户数据
! @+ ^( m3 ?1 F2 ]( X l* U: d dwFlags:波形数据的缓冲区的属性1 Z/ I t N! K: R0 Z+ d# h
dwLoops:播放循环的次数,仅用于播放控制中
s4 }7 C" K3 J lpNext和reserved均为保留值
' E. J4 p- }, a K 注意:上述结构体以及我们在程序中所使用到的“HWAVEIN””HWAVEOUT”结构体均是系统已经存在的,我们只需要对其进行赋值即可。
. |! n* f7 \8 c* Q 概念3、消息处理函数
6 a2 y0 ?9 j l' ~, ^+ A MM_WIM_OPEN //设备的打开
0 N1 Z; K( L+ D4 l: T3 `' t! G. Q MM_WIM_DATA //设备数据的采集及操作
: a, c" c! A# R# g! s) m MM_WIM_CLOSE //设备的关闭& I9 ~8 B4 E5 [% y4 ^
相应回放设备的消息分别为MM_WOM_OPEN,MM_WOM_DATA,MM_WOM_CLOSE.+ X- E N4 r" k( k
注意:消息处理函数是消息自我驱动的,不需要我们的人为干预。比如:当我们打开设备时,系统会自动调用MM_WIM_OPEN,当我们将数据添加到缓冲区,而缓冲区满时,系统会自动调用MM_WIM_DATA,我们所需要做的,就是对该函数编好相应的源代码。2 o' g1 Q8 ]1 d0 x0 I0 W
现在,我们进入正题:如何实现一个录音机。
) s! j: f; h% V0 l; ` q
& A. q2 Q! p- B+ N( j ⑴先对WAVEFORMATEX结构体进行赋值,然后为缓冲区分配内存pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);' A g9 I" k! k1 ?" o; I. `% m
pBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);
) q, H/ n6 L+ Z- p3 n对设备头结构体分配内存pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
2 L- R0 Z$ b' i' I6 f2 ZpWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));$ [: q6 f W6 e) _( P A# l x& a# Q
然后使用wave音频相关函数对输入数据进行操作:
4 _2 `3 j, V( W! T" G6 ]0 F6 |) Z6 y1 @+ A
①为波形输入设备准备缓冲区waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));
: \, a6 v' I G# S* p! twaveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));% ]0 Y0 s4 g3 W+ Y
②为波形输入设备添加缓冲区waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;
: }! X& @, w; w( s- I0 OwaveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;8 n, [& h2 `0 {
③启动声音输入设备,将输入数据写入内存waveInStart (hWaveIn) ;⑵编写消息处理函数,其中,MM_WIM_DATA函数是本程序的核心。其主要作用是将输入数据另行保存在一缓冲区内(pSaveBuffer),该缓冲区的长度将随着已录入数据的大小而增加,从而实现保存输入话音数据的功能。同时,可将缓冲区内数据保存为wav文件。其具体实现如下:CFile m_file;/ q3 t. k% H+ ^ p
CFileException fileException;! c4 e& O+ E: u
CString m_csFileName= "F:\\audio.wav";//保存路径
& f. l7 t6 ^6 W: K8 j9 u6 t6 ]. b/ Z1 pm_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);
: A1 ?9 H4 M) w- S$ DDWORD m_WaveHeaderSize = 38;% J1 W: l0 m/ |; i1 L/ C; b
DWORD m_WaveFormatSize = 18;
1 ~+ ]" e! ~! @8 em_file.SeekToBegin();" \6 z9 }# j0 K: I9 T/ Y) y
m_file.Write("RIFF",4);
6 c7 N# E$ a3 z. S& Wunsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize);
2 b/ \' c; d. z5 u; \ @; \m_file.Write(&Sec,sizeof(Sec));+ v; L8 i( \9 k+ U3 {/ q
m_file.Write("WAVE",4);
, M, }$ p+ r* h0 im_file.Write("fmt ",4); u# D* }& Y- }1 U! d; c
m_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));
5 g/ q }1 t" ^% |( y- x) A Cm_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag));
0 h6 X; ~* E% i" s; fm_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));
g* ]. P0 A8 p& c: R2 H5 V2 Sm_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));
+ \5 c( G% `* Z0 fm_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));
% h$ V) [6 f) k; _$ h0 L) wm_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));
* J; |- J+ i7 mm_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));
1 `% H* T0 l( o) ], j: ~' W& Sm_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));
6 q& I# j( z5 J4 D5 em_file.Write("data",4);: h0 N V: U- a9 D! T6 a
m_file.Write(&dwDataLength,sizeof(dwDataLength));- P+ X& b0 V8 i3 L5 b( B
m_file.Write(pSaveBuffer,dwDataLength);
/ \# m( y7 z' K8 Em_file.Seek(dwDataLength,CFile::begin);+ z( k6 d$ H& b: C l
m_file.Close();7 {$ Y1 Y1 V; O0 J. I3 L0 v
存在的问题:
+ E. Y% ?+ T! e, v+ O2 `. b i& X我们也才开始vc学习,只是将自己了解的知识与和我们水平相当的新手们分享,希望一起提高。本程序有时不稳定,但音质很好,可能还有尚未暴露的错误,恳请广大高手们不吝赐教。E-mail: xuejing0103@126.com,smilewenjuan@yahoo.com.cn9 V- s' u/ ?9 c
0 B' s# z8 c: v原文:http://www.vckbase.com/document/viewdoc/?id=1800 |
|