|
|
|
作者:任雪景、文娟
' l1 h' E3 C1 A9 j' b( @6 w 下载源代码
( x7 c& Y) c" m( Q+ ]. }' A ! M5 B$ g, r3 M! U/ x
引言0 F6 [* S( R0 k# R
我是C++的初学者,入门都要靠VCKBASE,好在里面有很多适合于初学者的例子,让我少走了很多弯路,为了回馈大家,我也把我最近刚完成的一个简单的小程序提供给大家,让那些曾经和我一样徘徊在C++大门之外的人能快些掌握要领,大家不妨看一看。
) m2 T1 G/ f* H9 ]1 g" v/ ?+ V 本文以VC知识库第26期 栾义明 先生的《基于API的录音机程序》为基础的,在此深表感谢!相同之处不再重复,我在此基础上增加了将录音保存为wav文件的格式,便于大家参考。
4 P; o) V6 \; M; ?基本步骤及思想:设定音频采集参数(采样率、声道等),打开音频设备、准备wave数据头和开辟缓存,操作采集的数据并保存为wav文件。设定音频回放参数,打开回放设备、准备wave数据头和写wave数据。另外样本程序需包含#include <mmsystem.h>和#pragmacomment(lib,"WINMM.LIB")多媒体支持。1 A6 s. [, N$ w: ~; d" |
在介绍程序前,需要你对wave文件的格式和相关一些基础概念有所了解,这些均可以在msdn中查找,为方便理解,我们将其整理,如果对这些基础知识已有所了解,可以跳过。
9 h6 O- e y- U1 G& [
+ K$ j5 h5 O- s( P7 @0 _ 概念1、定义波形数据格式
6 B1 a* f/ n. l% M7 a7 | typedef struct{WORD wFormatTag; 8 d9 z/ A) S4 V3 {6 s4 T5 k& t
WORD nChannels; * t4 H- J9 c* N& l, Q3 |
DWORD nSamplesPerSec; , e2 j1 \* _& Z+ D! f
DWORD nAvgBytesPerSec;- i% L3 Q& `0 E0 O
WORD nBlockAlign; . }( }4 l' o9 a& K% z; e9 h7 M
WORD wBitsPerSample;
$ b& ^$ ~6 U' [- A0 i. I+ c8 l WORD cbSize; } WAVEFORMATEX; 具体参数解释如下:
0 e- b2 T. j' q7 G3 ^9 R5 H O wFormatTag:波形数据的格式,定义在MMREG.H文件中$ \/ I( u5 l( U. q- J* H- [8 l y
nChannels:波形数据的通道数:单声道或立体声% @7 J5 M" s' y; p5 ?
nSamplesPerSec:采样率,对于PCM格式的波形数据,采样率有8.0 kHz,11.025kHz,22.05 kHz,44.1 kHz等/ T7 Q, d7 l% d9 h7 D" y) C$ C' m
nAvgBytesPerSec:数据率,对于PCM格式的波形数据,数据率等于采样率乘以每样点字节数 H& U, L- d$ [5 V
nBlockAlign:每个样点字节数
1 `4 I. q. { W wBitsPerSample:采样精度,对于PCM格式的波形数据,采样精度为8或16
& P% t# |9 E/ F cbSize:附加格式信息的数据块大小7 K, C( G& m$ L' y' p7 a3 H+ v
概念2、定义设备头结构, H' L: O' ` b Z
WAVEHDR定义了指向波形数据缓冲区的设备头。typedef struct { LPSTR lpData; 0 N4 Q5 M7 u3 G9 w: n5 J/ z# u
DWORD dwBufferLength; $ X) o- Q7 z- F3 g
DWORD dwBytesRecorded;
7 F* B, a3 P; x% I DWORD dwUser;
" k- z# a) _7 F2 A% \: D+ ^6 _! v* U DWORD dwFlags; . h- |+ ^+ K3 Z. L9 I5 a
DWORD dwLoops; 5 o+ {5 d3 \( k8 C
struct wavehdr_tag * lpNext; 0 Z6 \8 I' U9 I/ s. h4 D9 w( e1 l
DWORD reserved; } WAVEHDR; lpData:波形数据的缓冲区地址0 S- b& P8 `- D h* B- Y% S7 d" G
dwBufferLength:波形数据的缓冲区地址的长度0 a, C% d3 W3 w/ _% |
dwBytesRecorded:当设备用于录音时,标志已经录入的数据长度3 w1 ?% f$ \; k. M, d8 y. j
dwUser:用户数据
1 K! D P; o+ F/ Y/ y2 W" I6 R# j dwFlags:波形数据的缓冲区的属性+ {* Q7 G4 l; y; X6 }
dwLoops:播放循环的次数,仅用于播放控制中' u; Y' d; M) [" a% t. P N
lpNext和reserved均为保留值- ~$ K" y$ Y/ M1 d5 N
注意:上述结构体以及我们在程序中所使用到的“HWAVEIN””HWAVEOUT”结构体均是系统已经存在的,我们只需要对其进行赋值即可。
4 U# `9 [6 ^5 q2 D$ y% ^9 ^8 { 概念3、消息处理函数
- u, B* ^5 s1 v0 o& M MM_WIM_OPEN //设备的打开
8 M* R7 ?8 V% V t MM_WIM_DATA //设备数据的采集及操作7 s. ^& x, F* ]) b3 T
MM_WIM_CLOSE //设备的关闭
0 I$ Z; a& D% l& ] 相应回放设备的消息分别为MM_WOM_OPEN,MM_WOM_DATA,MM_WOM_CLOSE.- F; O1 ]- [3 ~- z
注意:消息处理函数是消息自我驱动的,不需要我们的人为干预。比如:当我们打开设备时,系统会自动调用MM_WIM_OPEN,当我们将数据添加到缓冲区,而缓冲区满时,系统会自动调用MM_WIM_DATA,我们所需要做的,就是对该函数编好相应的源代码。4 j% h; |) _# V0 h) L" `. _4 I. s
现在,我们进入正题:如何实现一个录音机。 k$ ]4 n/ W! }, X7 m. g& K
- E- ?; p m1 O: P: I2 ~ ⑴先对WAVEFORMATEX结构体进行赋值,然后为缓冲区分配内存pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);
, {" ], G# D2 p. C9 W- CpBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);
4 C# ?5 G3 E/ j. r% G* w2 F. @" v+ b对设备头结构体分配内存pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
$ I3 ]% c- `$ E# Q( ipWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));- e1 Y, Y, r# p4 |0 K% ^
然后使用wave音频相关函数对输入数据进行操作:- t9 `+ Z+ Y! q
4 S$ h1 m5 a( z7 n+ I7 }9 w7 ~- L7 w4 v
①为波形输入设备准备缓冲区waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));
9 i! Y. H4 {2 j( g6 ~# o1 @4 swaveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));
6 |+ h; [/ g' i0 S0 U②为波形输入设备添加缓冲区waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;& Z; j2 W9 y, w9 P0 n
waveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;
- D6 P! V; T6 L1 K1 a* l③启动声音输入设备,将输入数据写入内存waveInStart (hWaveIn) ;⑵编写消息处理函数,其中,MM_WIM_DATA函数是本程序的核心。其主要作用是将输入数据另行保存在一缓冲区内(pSaveBuffer),该缓冲区的长度将随着已录入数据的大小而增加,从而实现保存输入话音数据的功能。同时,可将缓冲区内数据保存为wav文件。其具体实现如下:CFile m_file;
& Y- G9 Z, q) @2 @CFileException fileException;. I; }) G) }& Q1 b7 `) t0 }
CString m_csFileName= "F:\\audio.wav";//保存路径
. T; p; `8 B3 Q' d. gm_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);3 E# U% r* A# k0 ]- Y; s6 d
DWORD m_WaveHeaderSize = 38;
1 b$ w. W' {; X/ g; T' k: tDWORD m_WaveFormatSize = 18;
Q3 M9 R4 \5 H1 Cm_file.SeekToBegin();/ t$ v1 i4 S- C
m_file.Write("RIFF",4);
$ [( A f$ e9 ~% N7 @ }8 Sunsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize); w ]8 h2 K" i' I$ l
m_file.Write(&Sec,sizeof(Sec));- }& v+ Y3 X5 B1 D8 [
m_file.Write("WAVE",4);
6 f' M7 w, [+ i) Y) x: C/ `1 j/ jm_file.Write("fmt ",4);
+ u! t) z7 v, E6 ^- n4 rm_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));
/ g: P4 s+ j& em_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag));
$ { K9 ^( E+ I' u7 P$ G$ O3 vm_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));7 c& b; M, D1 ^# T
m_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));/ w; u* N/ P' Q B
m_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));
5 C) H. X! g O9 hm_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));
2 g1 F0 L9 y/ n8 `m_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));
. P6 b$ ]* ?/ V- V" N$ qm_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));
& L# D" l# i- D9 J' am_file.Write("data",4);
: i1 f1 n# y A% \; E9 M( [- bm_file.Write(&dwDataLength,sizeof(dwDataLength));
7 v. A2 S& n/ zm_file.Write(pSaveBuffer,dwDataLength);$ \% ?! z) h* W$ M) ~+ O5 |8 q
m_file.Seek(dwDataLength,CFile::begin);8 i! B( S+ R, W- L" H# w; W
m_file.Close();( S ]0 v, X3 G6 J
存在的问题:' y6 x$ a P# a& H
我们也才开始vc学习,只是将自己了解的知识与和我们水平相当的新手们分享,希望一起提高。本程序有时不稳定,但音质很好,可能还有尚未暴露的错误,恳请广大高手们不吝赐教。E-mail: xuejing0103@126.com,smilewenjuan@yahoo.com.cn/ ] x0 _! b7 y0 U+ r% c
1 J; k( N; ~3 Q, n1 { N& ~原文:http://www.vckbase.com/document/viewdoc/?id=1800 |
|