|
|
|
作者:任雪景、文娟 7 [; [( t3 m. e ]) B, `: M, P
下载源代码
3 Y& }3 x- n5 g6 t4 h( u : @3 b" m; ?4 S1 S, `- {) E, A6 h
引言2 f5 ?) o7 V$ }! g3 Q
我是C++的初学者,入门都要靠VCKBASE,好在里面有很多适合于初学者的例子,让我少走了很多弯路,为了回馈大家,我也把我最近刚完成的一个简单的小程序提供给大家,让那些曾经和我一样徘徊在C++大门之外的人能快些掌握要领,大家不妨看一看。5 Z; s( {. e* g+ o6 S! ~, o5 T
本文以VC知识库第26期 栾义明 先生的《基于API的录音机程序》为基础的,在此深表感谢!相同之处不再重复,我在此基础上增加了将录音保存为wav文件的格式,便于大家参考。" z, B; S; p# l" B
基本步骤及思想:设定音频采集参数(采样率、声道等),打开音频设备、准备wave数据头和开辟缓存,操作采集的数据并保存为wav文件。设定音频回放参数,打开回放设备、准备wave数据头和写wave数据。另外样本程序需包含#include <mmsystem.h>和#pragmacomment(lib,"WINMM.LIB")多媒体支持。
6 W; g, c8 q' D" s3 ?; y# D 在介绍程序前,需要你对wave文件的格式和相关一些基础概念有所了解,这些均可以在msdn中查找,为方便理解,我们将其整理,如果对这些基础知识已有所了解,可以跳过。, a3 k' l; r5 N$ q, v; V' k; d
! H* j- m3 k3 V5 c' M! G
概念1、定义波形数据格式 U: C' [, M( |
typedef struct{WORD wFormatTag;
% U! x% ? U# H, \' y6 b3 [1 D WORD nChannels;
' |* j5 D' E9 h0 [9 \ DWORD nSamplesPerSec; # B& z- a0 O: v% l+ {5 h
DWORD nAvgBytesPerSec;
6 j7 [) y" d( C1 v' q9 R WORD nBlockAlign; - b! }# Z/ u; j2 ]7 Z: m9 Z
WORD wBitsPerSample; 9 ]; y* r/ I& u: p2 M2 m- \
WORD cbSize; } WAVEFORMATEX; 具体参数解释如下:
6 Z! }% t- ~! S& K wFormatTag:波形数据的格式,定义在MMREG.H文件中
' @6 [" \% |3 _# E/ T7 ]* ` nChannels:波形数据的通道数:单声道或立体声- o- x4 S9 I' Z
nSamplesPerSec:采样率,对于PCM格式的波形数据,采样率有8.0 kHz,11.025kHz,22.05 kHz,44.1 kHz等9 I$ S+ }8 m6 q! V( H3 r
nAvgBytesPerSec:数据率,对于PCM格式的波形数据,数据率等于采样率乘以每样点字节数
p- n- {: ?) S& \7 v0 ^9 j nBlockAlign:每个样点字节数: g. L: {$ K% G0 `% t3 y( {
wBitsPerSample:采样精度,对于PCM格式的波形数据,采样精度为8或16
0 h3 [. g/ e, D! Y) m cbSize:附加格式信息的数据块大小
) f2 Z* U: W- a 概念2、定义设备头结构
+ X s/ s C) c5 q2 r WAVEHDR定义了指向波形数据缓冲区的设备头。typedef struct { LPSTR lpData; 5 ~* m7 p1 j6 [4 }
DWORD dwBufferLength; + _3 ? C- ?- U0 i5 m, d4 C! d+ }
DWORD dwBytesRecorded; 3 u! T# Y' ?5 B# T7 N t
DWORD dwUser; . J4 f9 e8 ]- e
DWORD dwFlags;
) n$ E7 f/ E3 D" N0 d- ^! Z DWORD dwLoops; * ^; ~% r8 f8 {8 a- m6 @
struct wavehdr_tag * lpNext; # H( W' w: G: F# n7 W2 s( p
DWORD reserved; } WAVEHDR; lpData:波形数据的缓冲区地址
) H" D+ w6 ]4 M, i) P4 q, S dwBufferLength:波形数据的缓冲区地址的长度
1 b' u1 [! [1 \1 I) r- {# l& U+ M dwBytesRecorded:当设备用于录音时,标志已经录入的数据长度
. C3 ^, b, g2 u* S3 h( X c dwUser:用户数据) b9 X3 z! {3 l3 J/ v! U3 W* z
dwFlags:波形数据的缓冲区的属性
( t5 P6 V. H9 n0 M- G) W) z dwLoops:播放循环的次数,仅用于播放控制中
, z1 n" J8 v3 M2 z) j* m lpNext和reserved均为保留值
Q( w8 ^* F: d( C* H 注意:上述结构体以及我们在程序中所使用到的“HWAVEIN””HWAVEOUT”结构体均是系统已经存在的,我们只需要对其进行赋值即可。
8 g1 V- w. n2 F+ s2 C4 P 概念3、消息处理函数
/ B+ r$ x! E7 n3 g7 s MM_WIM_OPEN //设备的打开" w+ J" x. {) t5 f$ @- V
MM_WIM_DATA //设备数据的采集及操作
8 W+ u& ]% w4 D: G( b/ x4 E! c0 q1 r MM_WIM_CLOSE //设备的关闭# A1 U% x' P* q: Z
相应回放设备的消息分别为MM_WOM_OPEN,MM_WOM_DATA,MM_WOM_CLOSE.
f5 B5 d) x7 g! T/ m5 G 注意:消息处理函数是消息自我驱动的,不需要我们的人为干预。比如:当我们打开设备时,系统会自动调用MM_WIM_OPEN,当我们将数据添加到缓冲区,而缓冲区满时,系统会自动调用MM_WIM_DATA,我们所需要做的,就是对该函数编好相应的源代码。# C* G9 `7 N; w0 }
现在,我们进入正题:如何实现一个录音机。5 b# { x/ i/ l8 K! e9 H; g2 E
2 L. ~1 A/ D6 e1 B6 P# ?! t1 T
⑴先对WAVEFORMATEX结构体进行赋值,然后为缓冲区分配内存pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);
' N8 n; G4 g6 q* s |pBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);- `( C( o9 A! r, h: _4 T- |9 z
对设备头结构体分配内存pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));2 Z$ q2 T7 c, F. g' S" h) ^
pWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
5 K( k8 y5 l6 \& b然后使用wave音频相关函数对输入数据进行操作:
, q8 p1 s: C0 g* `: {' \1 Z9 t
/ t# k7 ?0 X" U0 h①为波形输入设备准备缓冲区waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));
' d+ y$ C1 A( q8 w5 RwaveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));
; y- f; o) }2 u1 G( @7 Q②为波形输入设备添加缓冲区waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;
' X/ f' |0 t. K+ X1 mwaveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;2 r8 l% }7 J0 f: G9 T
③启动声音输入设备,将输入数据写入内存waveInStart (hWaveIn) ;⑵编写消息处理函数,其中,MM_WIM_DATA函数是本程序的核心。其主要作用是将输入数据另行保存在一缓冲区内(pSaveBuffer),该缓冲区的长度将随着已录入数据的大小而增加,从而实现保存输入话音数据的功能。同时,可将缓冲区内数据保存为wav文件。其具体实现如下:CFile m_file;5 v0 ?- d/ \7 R M+ C7 M
CFileException fileException;/ P+ B, m- p) a, n4 j3 T! G
CString m_csFileName= "F:\\audio.wav";//保存路径
- v: v$ e$ G% Dm_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);
9 [# J5 S# U% h& ADWORD m_WaveHeaderSize = 38;
+ Q4 r8 F% ^% q% jDWORD m_WaveFormatSize = 18;8 O4 s$ _1 O9 ~8 L! b
m_file.SeekToBegin();$ h- @: N4 h0 }) F8 M) G
m_file.Write("RIFF",4);
3 W7 T( h& F4 ^6 `9 d1 funsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize);" G7 b$ Y6 f# X8 S$ N
m_file.Write(&Sec,sizeof(Sec));1 w- a' ?, e: ? g) f7 M7 k5 i3 v+ J
m_file.Write("WAVE",4);
k* d/ \6 y5 J1 ]: H* Jm_file.Write("fmt ",4);
) O$ P, C( l' H/ am_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));
1 x7 k1 h, x( E6 C) i3 Dm_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag));
; f2 J( e7 g* D# D& f; [& Cm_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));
5 ~# Q* `2 ?3 b1 Tm_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));
# e. C$ Z, l& N7 c$ \% C3 S, om_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));/ n+ f8 I4 _) ]5 i% Z& o
m_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));. q9 |, \+ C2 \# w1 R& v4 X
m_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));
8 o3 N! L- i' c9 C& ~$ im_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));
- L& E& T: H8 D6 P$ a. y3 bm_file.Write("data",4);5 B& A1 Y l5 Z
m_file.Write(&dwDataLength,sizeof(dwDataLength));, g8 d1 V" o% `/ B1 \/ |9 H
m_file.Write(pSaveBuffer,dwDataLength);% L% v3 R' B: ~/ z, z- ]" F% ^
m_file.Seek(dwDataLength,CFile::begin);
. X1 Q# y( O2 d2 p, e/ v hm_file.Close();: w& t. g# ~' F0 z
存在的问题:
6 a; |6 l2 g \: i4 U我们也才开始vc学习,只是将自己了解的知识与和我们水平相当的新手们分享,希望一起提高。本程序有时不稳定,但音质很好,可能还有尚未暴露的错误,恳请广大高手们不吝赐教。E-mail: xuejing0103@126.com,smilewenjuan@yahoo.com.cn
! d( Q3 P, @# f& A+ P1 o% x
4 l$ n2 e5 ]1 v: q; h/ G原文:http://www.vckbase.com/document/viewdoc/?id=1800 |
|