|
|
|
作者:任雪景、文娟
, E3 l# Y& a7 v7 r 下载源代码5 }- a8 K5 Z* F0 v- S9 `
$ i: z4 [% U" U+ \$ S) S# }" q 引言
# ?4 L* }4 A8 n5 l9 U. q" X* f 我是C++的初学者,入门都要靠VCKBASE,好在里面有很多适合于初学者的例子,让我少走了很多弯路,为了回馈大家,我也把我最近刚完成的一个简单的小程序提供给大家,让那些曾经和我一样徘徊在C++大门之外的人能快些掌握要领,大家不妨看一看。9 V& P* p+ D- {
本文以VC知识库第26期 栾义明 先生的《基于API的录音机程序》为基础的,在此深表感谢!相同之处不再重复,我在此基础上增加了将录音保存为wav文件的格式,便于大家参考。
1 [$ l8 t$ r) X \7 `基本步骤及思想:设定音频采集参数(采样率、声道等),打开音频设备、准备wave数据头和开辟缓存,操作采集的数据并保存为wav文件。设定音频回放参数,打开回放设备、准备wave数据头和写wave数据。另外样本程序需包含#include <mmsystem.h>和#pragmacomment(lib,"WINMM.LIB")多媒体支持。
4 y2 k6 H4 [1 T$ s4 ? 在介绍程序前,需要你对wave文件的格式和相关一些基础概念有所了解,这些均可以在msdn中查找,为方便理解,我们将其整理,如果对这些基础知识已有所了解,可以跳过。
; n6 c2 b$ ?. x % c Y6 O6 h" z) m# I6 |
概念1、定义波形数据格式
9 k9 u* F* [9 h7 o( A typedef struct{WORD wFormatTag; : E' g* r. U3 Z
WORD nChannels; 0 b! ^8 g( A) P2 I; Y" c+ e
DWORD nSamplesPerSec; % f- |2 V/ c5 d7 S& k5 H$ n1 W. l
DWORD nAvgBytesPerSec;$ k0 q" w- H, O4 a8 ^0 H
WORD nBlockAlign; . @0 T( v3 A) `; |- E& R' G5 i' P) J
WORD wBitsPerSample;
& Z9 ?/ b. R: y# Q! U* c WORD cbSize; } WAVEFORMATEX; 具体参数解释如下:: p- [9 Q6 u6 v' _2 J$ o
wFormatTag:波形数据的格式,定义在MMREG.H文件中5 d! T* S. C: _! G1 `
nChannels:波形数据的通道数:单声道或立体声2 X/ W. ?6 E+ L$ e% n
nSamplesPerSec:采样率,对于PCM格式的波形数据,采样率有8.0 kHz,11.025kHz,22.05 kHz,44.1 kHz等! {0 j; s8 e* o- B8 ^
nAvgBytesPerSec:数据率,对于PCM格式的波形数据,数据率等于采样率乘以每样点字节数$ N6 B9 _5 t) P7 n% E3 N
nBlockAlign:每个样点字节数( g( `3 L' ]8 U/ B# P! f- c
wBitsPerSample:采样精度,对于PCM格式的波形数据,采样精度为8或16
% r; K! F* m h( R4 V2 _0 j" a* r2 d cbSize:附加格式信息的数据块大小9 P( {+ d! E8 T+ Q4 t. {! ?
概念2、定义设备头结构; k1 b2 K4 t# T
WAVEHDR定义了指向波形数据缓冲区的设备头。typedef struct { LPSTR lpData; 9 j# r9 O7 a$ w8 w5 k
DWORD dwBufferLength;
* r/ \$ Z2 A; I6 a: E8 s+ G: { DWORD dwBytesRecorded; . @4 d2 A7 Y: Y. ?" O0 K
DWORD dwUser;
" u1 ~, H' p& k DWORD dwFlags; 0 S: h; @$ U% v" w: W
DWORD dwLoops;
1 Z( o9 s6 k5 K Z5 G( |: o struct wavehdr_tag * lpNext;
5 p e' v) U+ |' ` DWORD reserved; } WAVEHDR; lpData:波形数据的缓冲区地址8 k$ ^3 O( C" t. [+ a% P
dwBufferLength:波形数据的缓冲区地址的长度
: E9 P* m/ z& S7 g+ E* S: Q7 x dwBytesRecorded:当设备用于录音时,标志已经录入的数据长度# W+ N# s$ f: S8 f' f7 e/ S3 F+ ~5 @4 O
dwUser:用户数据0 |5 [( Z5 m9 }$ L
dwFlags:波形数据的缓冲区的属性
. E- e$ c3 e! p% I dwLoops:播放循环的次数,仅用于播放控制中* n- w2 L9 F5 K4 r$ Z* D
lpNext和reserved均为保留值
* ]; }6 D( k9 b8 _- \ 注意:上述结构体以及我们在程序中所使用到的“HWAVEIN””HWAVEOUT”结构体均是系统已经存在的,我们只需要对其进行赋值即可。8 f$ ?* ` J2 A3 ]5 i
概念3、消息处理函数) D0 v* G0 i5 u& U- y# s
MM_WIM_OPEN //设备的打开
: `% ?! d( M1 P" V; X: g& M4 S MM_WIM_DATA //设备数据的采集及操作. M6 N' L& X) V. Y" _
MM_WIM_CLOSE //设备的关闭7 d) ?, ^7 L& E" `
相应回放设备的消息分别为MM_WOM_OPEN,MM_WOM_DATA,MM_WOM_CLOSE.
' r! M6 A5 A$ ?- W5 i6 @ 注意:消息处理函数是消息自我驱动的,不需要我们的人为干预。比如:当我们打开设备时,系统会自动调用MM_WIM_OPEN,当我们将数据添加到缓冲区,而缓冲区满时,系统会自动调用MM_WIM_DATA,我们所需要做的,就是对该函数编好相应的源代码。6 V S$ K9 n" v: }7 w
现在,我们进入正题:如何实现一个录音机。
1 U/ V+ W8 x- ^+ O 1 [7 T: y# e6 [. X7 q1 T+ L1 \3 i
⑴先对WAVEFORMATEX结构体进行赋值,然后为缓冲区分配内存pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);3 g/ m H' D: ~; A% }1 ]
pBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);# ` B0 R3 C9 h# n# e% c( Z
对设备头结构体分配内存pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
0 k* A3 j( i5 NpWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));/ z* W3 ?9 L8 `4 j" b
然后使用wave音频相关函数对输入数据进行操作:5 c$ u" t# s+ a X: }( f
R d& W6 R3 n6 T' n ? u
①为波形输入设备准备缓冲区waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));1 K& g( a! m, l/ @
waveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));
' n }) u g# @+ v1 q, G②为波形输入设备添加缓冲区waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;
+ j1 r7 s3 p2 ? G) GwaveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;
1 H% h& ?0 F9 F, R- }③启动声音输入设备,将输入数据写入内存waveInStart (hWaveIn) ;⑵编写消息处理函数,其中,MM_WIM_DATA函数是本程序的核心。其主要作用是将输入数据另行保存在一缓冲区内(pSaveBuffer),该缓冲区的长度将随着已录入数据的大小而增加,从而实现保存输入话音数据的功能。同时,可将缓冲区内数据保存为wav文件。其具体实现如下:CFile m_file;
- o5 T! M4 ?9 Z. ^: XCFileException fileException;
+ W+ T* w4 l( v. l& k$ S5 GCString m_csFileName= "F:\\audio.wav";//保存路径9 E7 O: c- j% k& g- J/ z
m_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);
5 Q7 m. z! c8 c* DDWORD m_WaveHeaderSize = 38;
! ]) C: N/ |6 J' e& } X- QDWORD m_WaveFormatSize = 18;
5 E: b* s8 \) p4 g% x* qm_file.SeekToBegin();6 k* q8 G$ D& N7 r. u$ g
m_file.Write("RIFF",4);
. b4 ?# A3 c+ F2 w- V) x6 tunsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize);5 a4 T4 f+ E9 j2 C- q
m_file.Write(&Sec,sizeof(Sec));
" |" o9 @$ O1 b* E( w8 Q7 p) tm_file.Write("WAVE",4);. J% n) \" n9 L4 M5 {
m_file.Write("fmt ",4);
' _) I8 K$ H+ t- q; hm_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));; b4 f! w# N" G/ Y0 d
m_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag));$ s6 w( j( t) v
m_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));/ ^: o! P' ^7 M# G6 A9 {
m_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));
: X0 z2 E0 ^9 P$ vm_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));
6 I P3 ~7 g7 }& P( qm_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));; f+ Z* A7 Z1 Y, \6 z( k* P5 H
m_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));1 i4 I W/ Q0 u4 {8 t
m_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));- F2 c( Z5 G1 e" l6 p3 M( |
m_file.Write("data",4);
- v7 J5 J" i0 W) |, U( Nm_file.Write(&dwDataLength,sizeof(dwDataLength));
3 }* L3 F( h/ j, w8 M8 Dm_file.Write(pSaveBuffer,dwDataLength);
: C5 w0 m+ O0 w& e- t& rm_file.Seek(dwDataLength,CFile::begin);
2 y% z0 R2 @8 z7 W# rm_file.Close();# S. B; s% c! S$ x+ }$ g J
存在的问题:: ^# I5 C9 e& a, U9 ]
我们也才开始vc学习,只是将自己了解的知识与和我们水平相当的新手们分享,希望一起提高。本程序有时不稳定,但音质很好,可能还有尚未暴露的错误,恳请广大高手们不吝赐教。E-mail: xuejing0103@126.com,smilewenjuan@yahoo.com.cn6 c- [; C; w8 a- b7 G0 {
% @5 R! }, O C% P0 S
原文:http://www.vckbase.com/document/viewdoc/?id=1800 |
|