|
|
|
作者:任雪景、文娟 ! T8 }+ B7 r; u: v( k |
下载源代码
- U) Y# j2 K/ \7 D j3 q* Y1 Z7 V, L) a! W8 a4 G1 p
引言
( `& L# ]+ n' D$ \ 我是C++的初学者,入门都要靠VCKBASE,好在里面有很多适合于初学者的例子,让我少走了很多弯路,为了回馈大家,我也把我最近刚完成的一个简单的小程序提供给大家,让那些曾经和我一样徘徊在C++大门之外的人能快些掌握要领,大家不妨看一看。
$ `' p5 E: t: c$ e 本文以VC知识库第26期 栾义明 先生的《基于API的录音机程序》为基础的,在此深表感谢!相同之处不再重复,我在此基础上增加了将录音保存为wav文件的格式,便于大家参考。, \7 @- B6 d/ p1 Z1 _& c% B
基本步骤及思想:设定音频采集参数(采样率、声道等),打开音频设备、准备wave数据头和开辟缓存,操作采集的数据并保存为wav文件。设定音频回放参数,打开回放设备、准备wave数据头和写wave数据。另外样本程序需包含#include <mmsystem.h>和#pragmacomment(lib,"WINMM.LIB")多媒体支持。6 y ^- g/ ?/ O
在介绍程序前,需要你对wave文件的格式和相关一些基础概念有所了解,这些均可以在msdn中查找,为方便理解,我们将其整理,如果对这些基础知识已有所了解,可以跳过。& [! A& W9 E2 ~* S+ c
# ]) t L8 i* g( f N; Y 概念1、定义波形数据格式% Y, {0 n0 C% g( P
typedef struct{WORD wFormatTag;
" d1 V0 P3 v N WORD nChannels;
9 Y6 V. L# D+ O% S2 q. @ DWORD nSamplesPerSec; 0 `6 Y% [( r3 e- ` \8 K1 r( c
DWORD nAvgBytesPerSec;% s; x" r# O( o4 m6 K, H6 u ]
WORD nBlockAlign;
6 C1 \+ F$ O+ [2 [! Y/ F9 U/ _. Z WORD wBitsPerSample;
/ ^9 o# T7 ?) D. a: f WORD cbSize; } WAVEFORMATEX; 具体参数解释如下:
2 M% N4 t( u0 B, {) h2 l$ _" i wFormatTag:波形数据的格式,定义在MMREG.H文件中
. u: |! ]/ x& _1 A r nChannels:波形数据的通道数:单声道或立体声
% j H# W; j# O! M" N* ^# R nSamplesPerSec:采样率,对于PCM格式的波形数据,采样率有8.0 kHz,11.025kHz,22.05 kHz,44.1 kHz等2 x. v4 q0 n2 `% v3 l3 c
nAvgBytesPerSec:数据率,对于PCM格式的波形数据,数据率等于采样率乘以每样点字节数7 W- X* x/ ~5 @- x, i" I! ]5 C
nBlockAlign:每个样点字节数
1 E; m1 a4 v5 a1 j wBitsPerSample:采样精度,对于PCM格式的波形数据,采样精度为8或16, H% o: F4 k& ], O- \1 I
cbSize:附加格式信息的数据块大小6 {5 A* F2 D+ n4 ~/ E5 a
概念2、定义设备头结构, m. @6 j( O5 y1 b0 z2 X3 {/ E$ J5 E
WAVEHDR定义了指向波形数据缓冲区的设备头。typedef struct { LPSTR lpData; , _$ f- c& d4 M- t! U
DWORD dwBufferLength; , Q8 l9 O/ s: I/ y4 ~
DWORD dwBytesRecorded;
) f( h: @' P7 W& V7 o) \% t/ L DWORD dwUser; ( F' s o4 i m* Z+ b) _
DWORD dwFlags; 7 d$ h1 f5 u2 \8 @) r
DWORD dwLoops; 2 P; T* A5 y- v& v5 q/ `
struct wavehdr_tag * lpNext;
4 [+ e: H/ `1 ?5 d% _7 | DWORD reserved; } WAVEHDR; lpData:波形数据的缓冲区地址
1 @3 r) X4 O) Y- O dwBufferLength:波形数据的缓冲区地址的长度
& A/ k* d" V5 x dwBytesRecorded:当设备用于录音时,标志已经录入的数据长度- G0 n2 N4 j2 L, P! n
dwUser:用户数据, W, G8 R' O- u @4 X
dwFlags:波形数据的缓冲区的属性
/ [7 P1 Q" _+ l$ C- } dwLoops:播放循环的次数,仅用于播放控制中; l: c8 ~0 Y% X- O S: Q
lpNext和reserved均为保留值
9 m! D t, J1 S 注意:上述结构体以及我们在程序中所使用到的“HWAVEIN””HWAVEOUT”结构体均是系统已经存在的,我们只需要对其进行赋值即可。
4 d% m8 M4 }$ b( F' j. H8 d# [ 概念3、消息处理函数
( K& |& \/ a3 F( n' r MM_WIM_OPEN //设备的打开
% S$ H; x" g" g# w; _/ M MM_WIM_DATA //设备数据的采集及操作5 m) [6 T6 L- W/ h( G* l! ~7 a
MM_WIM_CLOSE //设备的关闭, W! `: |8 [: B" w3 j
相应回放设备的消息分别为MM_WOM_OPEN,MM_WOM_DATA,MM_WOM_CLOSE.7 t Z& S6 c- d4 p1 p' g( g7 G
注意:消息处理函数是消息自我驱动的,不需要我们的人为干预。比如:当我们打开设备时,系统会自动调用MM_WIM_OPEN,当我们将数据添加到缓冲区,而缓冲区满时,系统会自动调用MM_WIM_DATA,我们所需要做的,就是对该函数编好相应的源代码。( u6 M F; i' @3 K- M
现在,我们进入正题:如何实现一个录音机。
$ A$ u. W6 ^+ E$ f* [ , O; S* C0 v$ A/ i4 m: ~; ]
⑴先对WAVEFORMATEX结构体进行赋值,然后为缓冲区分配内存pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);
" n+ E3 r/ G7 x- `6 ^pBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);+ q& C4 l1 ~. E5 F4 G/ l5 H: c8 o* y- v
对设备头结构体分配内存pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
* {0 ^, |' [4 s" @pWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
+ G5 M% f: C. a+ C$ Y0 l4 R然后使用wave音频相关函数对输入数据进行操作:% U* A0 w& n8 z I% k2 |
- C3 }& n5 B4 ~& O0 @" Z①为波形输入设备准备缓冲区waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));" c2 p* U2 }; O. h% j4 D
waveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));
, B- K9 p) X* p% w4 ]+ k0 R. q②为波形输入设备添加缓冲区waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;
" t, E( N: A( W4 E3 \6 o- n& `waveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;
6 o t' `$ b9 F' q' a③启动声音输入设备,将输入数据写入内存waveInStart (hWaveIn) ;⑵编写消息处理函数,其中,MM_WIM_DATA函数是本程序的核心。其主要作用是将输入数据另行保存在一缓冲区内(pSaveBuffer),该缓冲区的长度将随着已录入数据的大小而增加,从而实现保存输入话音数据的功能。同时,可将缓冲区内数据保存为wav文件。其具体实现如下:CFile m_file;- L) T1 Q# k2 e# P& ^/ A
CFileException fileException;" K. c6 L' }3 j" o
CString m_csFileName= "F:\\audio.wav";//保存路径
, J B' j& E+ S. G. a$ a. _2 M) @m_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);
7 @* \, D) E6 q% R2 Q& `" FDWORD m_WaveHeaderSize = 38;
: V. _- ~. E# S7 A! ~' fDWORD m_WaveFormatSize = 18;/ k- B% C Y; p* [! v4 ^
m_file.SeekToBegin();
8 G1 R# O5 g$ q6 q* m# b, Im_file.Write("RIFF",4);4 }3 P- B# K0 j1 s) Y9 `0 g7 l( Q8 u/ {
unsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize);) c4 L- k3 Z) X- U" @2 L
m_file.Write(&Sec,sizeof(Sec));6 i' Q* P0 s1 P& e! q( f3 u4 Y: T
m_file.Write("WAVE",4);2 z2 d* ^6 E% o4 t
m_file.Write("fmt ",4);! L, h: g; ?4 r: E" P7 i% c% Z9 L
m_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));3 Q1 S. @% M; B& F; Z
m_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag)); @8 t- l. m7 A$ H$ k
m_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));7 Y0 h/ ]" R" O. }& C2 Z
m_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));5 E; O. u9 l+ O' @- ~- j7 V4 t
m_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));
}/ f- i4 o4 j2 |# d/ d7 hm_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));
! z6 J5 B( c4 k7 p- k2 Km_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));" m) E9 o) q" l6 F4 _
m_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));# n* M* d' d. A% H( y- q- N2 n
m_file.Write("data",4);
9 H+ T4 L2 w' ~7 c- Z# d# I8 C2 Wm_file.Write(&dwDataLength,sizeof(dwDataLength));
0 Z% s! z9 W/ z8 dm_file.Write(pSaveBuffer,dwDataLength);# T8 h- Q: q( R9 M7 W1 |
m_file.Seek(dwDataLength,CFile::begin);
0 k$ q2 k5 N B, n; hm_file.Close();) g2 @+ _' n, U# E# a- d% ?# s
存在的问题:5 `4 a! v5 @, o$ l$ U; F+ Z+ B1 r
我们也才开始vc学习,只是将自己了解的知识与和我们水平相当的新手们分享,希望一起提高。本程序有时不稳定,但音质很好,可能还有尚未暴露的错误,恳请广大高手们不吝赐教。E-mail: xuejing0103@126.com,smilewenjuan@yahoo.com.cn
, f( t0 o3 H" I- X; V9 E) t
8 ~6 O. z- u7 S/ p* w原文:http://www.vckbase.com/document/viewdoc/?id=1800 |
|