|
作者:任雪景、文娟
9 D+ ]# n3 X& a1 P+ p5 Q; q4 | 下载源代码3 W8 u5 v% |. p9 A
4 i `( F% }% J: Z5 ]
引言
; ]! ~- t: c* Y: Q1 x9 T+ k 我是C++的初学者,入门都要靠VCKBASE,好在里面有很多适合于初学者的例子,让我少走了很多弯路,为了回馈大家,我也把我最近刚完成的一个简单的小程序提供给大家,让那些曾经和我一样徘徊在C++大门之外的人能快些掌握要领,大家不妨看一看。
2 C/ u* Y4 I V3 ]% H2 o 本文以VC知识库第26期 栾义明 先生的《基于API的录音机程序》为基础的,在此深表感谢!相同之处不再重复,我在此基础上增加了将录音保存为wav文件的格式,便于大家参考。
4 \8 J& z: I' Q1 j- Q& u) F基本步骤及思想:设定音频采集参数(采样率、声道等),打开音频设备、准备wave数据头和开辟缓存,操作采集的数据并保存为wav文件。设定音频回放参数,打开回放设备、准备wave数据头和写wave数据。另外样本程序需包含#include <mmsystem.h>和#pragmacomment(lib,"WINMM.LIB")多媒体支持。
o7 b3 H" p8 |- g# `. X 在介绍程序前,需要你对wave文件的格式和相关一些基础概念有所了解,这些均可以在msdn中查找,为方便理解,我们将其整理,如果对这些基础知识已有所了解,可以跳过。
5 n {4 ~) K) V5 X8 j& W
; n& R5 c; e: R# n5 W# I 概念1、定义波形数据格式
7 o$ M" k6 ~6 I& g" f$ F3 N typedef struct{WORD wFormatTag;
" p5 C. X! C2 { WORD nChannels; 9 s/ |7 O* R) B
DWORD nSamplesPerSec;
3 V/ G @+ X6 ]( C9 W DWORD nAvgBytesPerSec;
$ _" {: u0 e% D# {& K* a' ~8 X WORD nBlockAlign;
3 w0 [3 }: i ^ WORD wBitsPerSample;
& B1 T2 M+ G! d0 V ]/ ` WORD cbSize; } WAVEFORMATEX; 具体参数解释如下:7 ?! v. c& K R0 {
wFormatTag:波形数据的格式,定义在MMREG.H文件中6 P8 k% f( I* V0 o( m
nChannels:波形数据的通道数:单声道或立体声$ s, R# z8 a. Y Q+ u: s0 E
nSamplesPerSec:采样率,对于PCM格式的波形数据,采样率有8.0 kHz,11.025kHz,22.05 kHz,44.1 kHz等
) D8 ^- l: n; r7 c f% Z8 S4 T; a nAvgBytesPerSec:数据率,对于PCM格式的波形数据,数据率等于采样率乘以每样点字节数/ o" C& ^+ h" p2 C# p4 m8 |
nBlockAlign:每个样点字节数! V1 w5 ]- i4 C" I G/ v. ?$ s
wBitsPerSample:采样精度,对于PCM格式的波形数据,采样精度为8或160 x1 X& Q. S9 Y+ j/ J
cbSize:附加格式信息的数据块大小! k- u$ [! a3 l& P3 V
概念2、定义设备头结构% D3 ?! V7 U& Z) i( c0 V& H
WAVEHDR定义了指向波形数据缓冲区的设备头。typedef struct { LPSTR lpData;
) V, B0 e4 a1 [( a/ U, W5 [5 ~" c DWORD dwBufferLength;
2 v+ T6 C% E( F* a7 K. H) K7 ? DWORD dwBytesRecorded;
Y( ` |; ^* |1 | DWORD dwUser;
) f$ D- S2 Z2 Z# h8 J) X7 J DWORD dwFlags;
* S) I; d& A- S1 A! Y# u DWORD dwLoops; : v) m: }. O* a* c0 X
struct wavehdr_tag * lpNext;
, V/ ?) [! d9 Z4 W! ?! @ DWORD reserved; } WAVEHDR; lpData:波形数据的缓冲区地址- V( d0 N+ a6 P8 q# z/ `# _! @) {
dwBufferLength:波形数据的缓冲区地址的长度
6 p5 |" l# K6 p, i+ r$ d dwBytesRecorded:当设备用于录音时,标志已经录入的数据长度
; |# h* Q6 y, L( B$ B1 i! V dwUser:用户数据: E, {4 q1 q! v- c8 Z; o
dwFlags:波形数据的缓冲区的属性
! g2 G$ U# f* U S1 A dwLoops:播放循环的次数,仅用于播放控制中
: X/ L' L: T( K9 x# e lpNext和reserved均为保留值7 Z1 h, |3 E8 q7 h8 y Y1 @
注意:上述结构体以及我们在程序中所使用到的“HWAVEIN””HWAVEOUT”结构体均是系统已经存在的,我们只需要对其进行赋值即可。
% {5 h" G# L3 b3 u' N$ U; B3 ~ 概念3、消息处理函数; V N) L8 V4 q( {6 {6 ?. M
MM_WIM_OPEN //设备的打开4 @' H# o, j" g) d) x# h
MM_WIM_DATA //设备数据的采集及操作
0 J3 C# Y, i1 q MM_WIM_CLOSE //设备的关闭( [6 s0 x$ z! n3 H; S( ^4 @
相应回放设备的消息分别为MM_WOM_OPEN,MM_WOM_DATA,MM_WOM_CLOSE.& x0 d/ I0 S O' l8 G
注意:消息处理函数是消息自我驱动的,不需要我们的人为干预。比如:当我们打开设备时,系统会自动调用MM_WIM_OPEN,当我们将数据添加到缓冲区,而缓冲区满时,系统会自动调用MM_WIM_DATA,我们所需要做的,就是对该函数编好相应的源代码。
: {/ g: p! r# Z" N2 v; w( w 现在,我们进入正题:如何实现一个录音机。
+ Y" W# V, L) @ r7 X8 ]" s * {' I7 I+ h: _3 s8 [# u
⑴先对WAVEFORMATEX结构体进行赋值,然后为缓冲区分配内存pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);
5 J3 H/ H" \9 Y" z, a ^pBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);
3 ?: w! I _0 Y; ~4 L3 Z对设备头结构体分配内存pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
/ C9 R8 G: ?3 u8 P/ jpWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
/ \, ^7 c) G' N* Z/ M& J4 x. g然后使用wave音频相关函数对输入数据进行操作:
4 q0 h: O( `: g5 U
8 L; F/ p3 i* _0 |①为波形输入设备准备缓冲区waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));2 p1 X8 b4 n" @
waveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));$ F. E; A6 w1 W4 c9 w* L0 Z
②为波形输入设备添加缓冲区waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;: @& c/ a: F0 Y9 U( u1 |
waveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;: o m- K* K* z, p& V$ a/ k4 I' o7 n2 w
③启动声音输入设备,将输入数据写入内存waveInStart (hWaveIn) ;⑵编写消息处理函数,其中,MM_WIM_DATA函数是本程序的核心。其主要作用是将输入数据另行保存在一缓冲区内(pSaveBuffer),该缓冲区的长度将随着已录入数据的大小而增加,从而实现保存输入话音数据的功能。同时,可将缓冲区内数据保存为wav文件。其具体实现如下:CFile m_file;3 o& I8 [! z7 M. f2 v
CFileException fileException;
0 W# W2 p0 @* H6 XCString m_csFileName= "F:\\audio.wav";//保存路径
" l/ @0 J: y/ n6 z% |) Nm_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);4 F' V5 l) }4 O; W1 j3 z: @" `
DWORD m_WaveHeaderSize = 38;
0 h: k3 r2 Z7 t% c. O5 xDWORD m_WaveFormatSize = 18;
9 R1 y8 e) n& x' c6 `; mm_file.SeekToBegin();' v0 C; G8 `' v7 W, x
m_file.Write("RIFF",4);
; s2 \/ [9 @0 c4 T+ Punsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize);
: [7 b( f- W5 g8 M6 C' ]0 {% O/ _m_file.Write(&Sec,sizeof(Sec)); J3 B7 \7 g* Q' k# `3 j$ K
m_file.Write("WAVE",4);; C( h1 a f0 O9 K ?
m_file.Write("fmt ",4);
. K8 q3 U- ?6 [m_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));
; C/ x7 K7 U6 `m_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag));1 b, _1 o$ p* G, M
m_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));
8 A1 L# w% j* p# {" W5 ]/ O8 w0 wm_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));, s! A& j/ @' x9 C4 v: b
m_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));4 q# F: f9 b% ^
m_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));7 C- O1 c( N/ Q+ h0 w$ o
m_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));
# I$ G$ y, f% c% U* Im_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));3 V, Z* O7 Q) }" V3 a
m_file.Write("data",4);
0 b' E" n1 U Xm_file.Write(&dwDataLength,sizeof(dwDataLength));, ?; x' U! a( E1 {2 c7 Q
m_file.Write(pSaveBuffer,dwDataLength);8 L1 d9 F2 b `: O
m_file.Seek(dwDataLength,CFile::begin);7 |# ~- \( _2 _ x5 ?
m_file.Close();
. M6 O' ?5 k+ v& e7 K* r存在的问题:: I2 G! Z4 Y1 Q. f7 K v
我们也才开始vc学习,只是将自己了解的知识与和我们水平相当的新手们分享,希望一起提高。本程序有时不稳定,但音质很好,可能还有尚未暴露的错误,恳请广大高手们不吝赐教。E-mail: xuejing0103@126.com,smilewenjuan@yahoo.com.cn
3 d# l% M; ]+ j9 `& e$ N) k# S& g# H! X8 E
原文:http://www.vckbase.com/document/viewdoc/?id=1800 |
|