|
作者:任雪景、文娟
4 N/ a/ w7 e! W: m( Z% Q 下载源代码
7 ~/ L$ V0 r' k( e4 t / p2 R- ?' o4 F5 ~6 p/ \( b
引言
# ~5 x9 z9 s1 K 我是C++的初学者,入门都要靠VCKBASE,好在里面有很多适合于初学者的例子,让我少走了很多弯路,为了回馈大家,我也把我最近刚完成的一个简单的小程序提供给大家,让那些曾经和我一样徘徊在C++大门之外的人能快些掌握要领,大家不妨看一看。0 Q9 c w: E/ E5 d/ i
本文以VC知识库第26期 栾义明 先生的《基于API的录音机程序》为基础的,在此深表感谢!相同之处不再重复,我在此基础上增加了将录音保存为wav文件的格式,便于大家参考。
' t3 P: g& F8 Q( V基本步骤及思想:设定音频采集参数(采样率、声道等),打开音频设备、准备wave数据头和开辟缓存,操作采集的数据并保存为wav文件。设定音频回放参数,打开回放设备、准备wave数据头和写wave数据。另外样本程序需包含#include <mmsystem.h>和#pragmacomment(lib,"WINMM.LIB")多媒体支持。
1 C! Y+ U* b& ~& z' ]8 ] 在介绍程序前,需要你对wave文件的格式和相关一些基础概念有所了解,这些均可以在msdn中查找,为方便理解,我们将其整理,如果对这些基础知识已有所了解,可以跳过。% W: h3 q5 y" r% D
, l4 \) `/ [8 d$ @4 v" k 概念1、定义波形数据格式/ F7 r7 b8 R6 A: n. Z8 N
typedef struct{WORD wFormatTag;
2 z8 r2 |9 z3 k$ W6 [ WORD nChannels; 9 r; {7 b$ L$ ~7 {) j
DWORD nSamplesPerSec; + K2 l" F/ t3 V; Q k9 n$ M
DWORD nAvgBytesPerSec;
- o1 s2 ?6 z; v2 e WORD nBlockAlign;
# _+ I1 g$ ^" p/ o) }1 S# T; T WORD wBitsPerSample;
% [* Y1 w2 g6 j; k) t4 } WORD cbSize; } WAVEFORMATEX; 具体参数解释如下:5 f" F* x! w- I' {7 W( p5 \, e# P7 q
wFormatTag:波形数据的格式,定义在MMREG.H文件中. Z# q. w& h8 F" |9 l4 e" k+ o
nChannels:波形数据的通道数:单声道或立体声7 ]2 g& P' ]8 i' w4 B9 O
nSamplesPerSec:采样率,对于PCM格式的波形数据,采样率有8.0 kHz,11.025kHz,22.05 kHz,44.1 kHz等
- H/ @- `; c* J nAvgBytesPerSec:数据率,对于PCM格式的波形数据,数据率等于采样率乘以每样点字节数( J; `. j( Y: n- d( X; R
nBlockAlign:每个样点字节数. w2 B, r& V9 X! _& L0 B" F/ L1 a
wBitsPerSample:采样精度,对于PCM格式的波形数据,采样精度为8或16& }( v' c% a- F4 H6 U
cbSize:附加格式信息的数据块大小4 p3 d- R7 B/ t9 y7 x4 a0 r& n' F
概念2、定义设备头结构. g3 B+ F A, w( _- a6 g* [
WAVEHDR定义了指向波形数据缓冲区的设备头。typedef struct { LPSTR lpData; / ^" m5 i) |) @# a) ^
DWORD dwBufferLength;
! ^) z/ M3 p, S# n3 o' J DWORD dwBytesRecorded;
: o, f& J& [: y9 ?% Y( ?& e+ z( x/ L DWORD dwUser; / H$ U% s$ }, M5 x1 V b- C( z
DWORD dwFlags; ( e& S( a* K* J# m y
DWORD dwLoops; # o! ?) _( v+ u& D0 k" y. L
struct wavehdr_tag * lpNext;
1 J9 o0 w! z5 R- Y9 r DWORD reserved; } WAVEHDR; lpData:波形数据的缓冲区地址( q8 K4 r% c- {
dwBufferLength:波形数据的缓冲区地址的长度7 z/ E1 z6 x N: {' i2 J9 B5 {+ t
dwBytesRecorded:当设备用于录音时,标志已经录入的数据长度& K. n3 ^* Z/ q/ ]. Q: x
dwUser:用户数据
9 ]6 u9 H# _; h4 w& K dwFlags:波形数据的缓冲区的属性
7 a- j0 |3 G4 O- V3 _* R' _/ x7 t dwLoops:播放循环的次数,仅用于播放控制中# q: f g* `7 c& t# [
lpNext和reserved均为保留值
: e: [3 E) T2 m0 c: c, p 注意:上述结构体以及我们在程序中所使用到的“HWAVEIN””HWAVEOUT”结构体均是系统已经存在的,我们只需要对其进行赋值即可。
3 B! H( b+ Q8 ^ 概念3、消息处理函数% V* D. h, m6 w& r
MM_WIM_OPEN //设备的打开% x# f0 M) |$ g6 X1 z
MM_WIM_DATA //设备数据的采集及操作
! `) Q8 k3 f& ?$ i; I, c' Z4 T# K MM_WIM_CLOSE //设备的关闭
( G; z2 v) d. ^" n9 C; T) _ 相应回放设备的消息分别为MM_WOM_OPEN,MM_WOM_DATA,MM_WOM_CLOSE.
8 I ]5 X" [5 O 注意:消息处理函数是消息自我驱动的,不需要我们的人为干预。比如:当我们打开设备时,系统会自动调用MM_WIM_OPEN,当我们将数据添加到缓冲区,而缓冲区满时,系统会自动调用MM_WIM_DATA,我们所需要做的,就是对该函数编好相应的源代码。
( s% F/ e. o0 T+ { c; \1 Y 现在,我们进入正题:如何实现一个录音机。, l9 w! g7 a, U4 ?3 H5 {
/ h7 s- Q( I5 u- B2 @
⑴先对WAVEFORMATEX结构体进行赋值,然后为缓冲区分配内存pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);
- G5 |( S0 o! |( p) ^( OpBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);$ w/ ~! G3 U0 \5 ]
对设备头结构体分配内存pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));7 ^3 S& _) _; s
pWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
; A. U8 B& P: x) @然后使用wave音频相关函数对输入数据进行操作:
C7 G2 r) E8 a* F# i9 t
2 a; x+ }8 n3 I" x- N9 E3 O9 c①为波形输入设备准备缓冲区waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));6 i% a: v" o9 C [4 |
waveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));
/ }& Q- ^9 l: `& b9 W7 i②为波形输入设备添加缓冲区waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ; P8 b f2 w( C" \( S4 }7 q
waveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;! m* d T0 j7 q
③启动声音输入设备,将输入数据写入内存waveInStart (hWaveIn) ;⑵编写消息处理函数,其中,MM_WIM_DATA函数是本程序的核心。其主要作用是将输入数据另行保存在一缓冲区内(pSaveBuffer),该缓冲区的长度将随着已录入数据的大小而增加,从而实现保存输入话音数据的功能。同时,可将缓冲区内数据保存为wav文件。其具体实现如下:CFile m_file;; P: f5 r1 E' G" Z4 x5 P# E
CFileException fileException;- k4 h# I/ l* S% q
CString m_csFileName= "F:\\audio.wav";//保存路径3 R3 h0 B/ D9 F/ A) p
m_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);- |1 f, |' E+ ~
DWORD m_WaveHeaderSize = 38;
, b8 q8 m7 R8 h' N2 u! D/ Y/ pDWORD m_WaveFormatSize = 18;
2 N* W# {; l( [& Gm_file.SeekToBegin();5 q2 K4 u3 y# a4 `, \/ X
m_file.Write("RIFF",4);
* q& n+ [1 d0 ^unsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize);
2 o- K! g+ i- C6 F' C7 Hm_file.Write(&Sec,sizeof(Sec));
7 U& x. ]0 [+ J' a2 n9 ^7 Y9 nm_file.Write("WAVE",4);7 E7 q6 r# p3 K
m_file.Write("fmt ",4);: b+ q% U9 z E+ S1 G
m_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));
$ V2 v& J1 U) F) U" h7 rm_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag));
% U) l: m5 S/ a( H8 g( ] ym_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));$ P( Z$ D) p$ j8 a
m_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));2 b9 y! V) j4 i0 l6 S
m_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));
- n2 |0 U1 O9 S$ f p3 Q& ~m_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));# D t& ] k1 s' w: n, M
m_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));
7 ~0 z2 x; d4 O/ P5 O0 M4 Hm_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));
- K* h, |5 y/ I, ~# y6 b* tm_file.Write("data",4);
2 Z+ V% @: r6 C! _9 Y% [3 r$ }0 _m_file.Write(&dwDataLength,sizeof(dwDataLength));6 e% \4 l2 D5 B0 y6 \9 ]
m_file.Write(pSaveBuffer,dwDataLength);% g% y2 B) ~4 m
m_file.Seek(dwDataLength,CFile::begin);! P) E# }5 J1 ?( v) R
m_file.Close();
9 Y7 l' {" l1 t存在的问题:( n5 [2 D. t* ~5 a; p+ m) g. w
我们也才开始vc学习,只是将自己了解的知识与和我们水平相当的新手们分享,希望一起提高。本程序有时不稳定,但音质很好,可能还有尚未暴露的错误,恳请广大高手们不吝赐教。E-mail: xuejing0103@126.com,smilewenjuan@yahoo.com.cn ^+ v( H. @3 ~- R. G. g" T
+ P/ i, Y9 D/ \1 q
原文:http://www.vckbase.com/document/viewdoc/?id=1800 |
|