|
|
|
作者:任雪景、文娟
8 E/ d! t) s4 F" Y, k 下载源代码
7 u" n+ w; @: Q
, t8 |, Q a# y1 m0 @6 L3 O6 O 引言
^, S# L6 O0 a# H3 K' H 我是C++的初学者,入门都要靠VCKBASE,好在里面有很多适合于初学者的例子,让我少走了很多弯路,为了回馈大家,我也把我最近刚完成的一个简单的小程序提供给大家,让那些曾经和我一样徘徊在C++大门之外的人能快些掌握要领,大家不妨看一看。
8 @/ o( ?4 _# g8 M6 a% `7 I 本文以VC知识库第26期 栾义明 先生的《基于API的录音机程序》为基础的,在此深表感谢!相同之处不再重复,我在此基础上增加了将录音保存为wav文件的格式,便于大家参考。
2 t$ v& l% i( H! \& [& |$ Q, h基本步骤及思想:设定音频采集参数(采样率、声道等),打开音频设备、准备wave数据头和开辟缓存,操作采集的数据并保存为wav文件。设定音频回放参数,打开回放设备、准备wave数据头和写wave数据。另外样本程序需包含#include <mmsystem.h>和#pragmacomment(lib,"WINMM.LIB")多媒体支持。0 t, p: ^3 C# m9 w
在介绍程序前,需要你对wave文件的格式和相关一些基础概念有所了解,这些均可以在msdn中查找,为方便理解,我们将其整理,如果对这些基础知识已有所了解,可以跳过。
1 L; \5 y4 E( U% C, V
h- F3 m0 U; A 概念1、定义波形数据格式
3 @! s4 Y8 R3 J. { typedef struct{WORD wFormatTag;
) P7 p9 g% S1 P" H9 l+ c( e WORD nChannels;
4 v4 D' D! I+ D6 a+ y' y DWORD nSamplesPerSec;
% Z: \4 s3 w( e/ P* f DWORD nAvgBytesPerSec;0 Z& Y$ w' E# \: k Q' G
WORD nBlockAlign;
2 w; S9 L3 e" R- r! R' ]; X1 ] w0 E3 ] WORD wBitsPerSample; ' P) R3 Q: N; N6 O; w0 T+ w1 E e
WORD cbSize; } WAVEFORMATEX; 具体参数解释如下:
" f8 n5 \& \+ y8 W; s wFormatTag:波形数据的格式,定义在MMREG.H文件中
3 R- T$ W" S! F9 J5 t) Y, G nChannels:波形数据的通道数:单声道或立体声* G) {' y) Z6 Q
nSamplesPerSec:采样率,对于PCM格式的波形数据,采样率有8.0 kHz,11.025kHz,22.05 kHz,44.1 kHz等
3 Y: n; A( r0 l, m; c. A$ W1 X4 x nAvgBytesPerSec:数据率,对于PCM格式的波形数据,数据率等于采样率乘以每样点字节数( f0 P& Q# _) _) N. `
nBlockAlign:每个样点字节数: Q1 F W9 A: \/ T
wBitsPerSample:采样精度,对于PCM格式的波形数据,采样精度为8或16
- P$ D/ c- W. W% F cbSize:附加格式信息的数据块大小9 C0 |/ [! r/ Y- ~6 g
概念2、定义设备头结构
$ ]& X2 y5 m" T! g! t7 j+ o! { WAVEHDR定义了指向波形数据缓冲区的设备头。typedef struct { LPSTR lpData;
( m" K3 I& ]5 g" d7 }1 p DWORD dwBufferLength; 7 l+ g0 ^, g5 P5 I9 x4 j& {
DWORD dwBytesRecorded;
7 i" r$ w0 J6 b DWORD dwUser;
9 O) q" ^# I) Z$ w DWORD dwFlags; # c3 g6 k7 n2 s3 Q& z3 U
DWORD dwLoops;
+ y/ ]( g' T- I3 O% N struct wavehdr_tag * lpNext;
, G, z8 S! q ~) g1 A3 w2 Z DWORD reserved; } WAVEHDR; lpData:波形数据的缓冲区地址0 M- M6 D' P# n9 O; _* Z1 U+ x: @
dwBufferLength:波形数据的缓冲区地址的长度
( s0 u2 c x I# ?0 i4 [# v7 _) {+ @+ ~" j dwBytesRecorded:当设备用于录音时,标志已经录入的数据长度
; d! p5 o0 h. g/ p dwUser:用户数据
- U, l, f1 j1 g, G7 o dwFlags:波形数据的缓冲区的属性/ [) H" u4 `% W- v9 `$ c' W
dwLoops:播放循环的次数,仅用于播放控制中' t! ~' B( n! i1 Y
lpNext和reserved均为保留值
3 a6 R4 ]& [- T# R3 ~9 O* q" H 注意:上述结构体以及我们在程序中所使用到的“HWAVEIN””HWAVEOUT”结构体均是系统已经存在的,我们只需要对其进行赋值即可。1 M- H6 f4 R" z8 _8 w1 c* h7 h1 s
概念3、消息处理函数
$ C) s' W6 ?; j% ^; P3 a MM_WIM_OPEN //设备的打开3 G: u5 J9 n4 ~( j
MM_WIM_DATA //设备数据的采集及操作
4 |9 }! A& g4 Z' n# e. S1 N1 z# B9 A MM_WIM_CLOSE //设备的关闭0 e. X8 ~4 x9 c$ g3 P- Z
相应回放设备的消息分别为MM_WOM_OPEN,MM_WOM_DATA,MM_WOM_CLOSE.
( U" S$ D( H) \! w# O6 U. L5 I* n/ { 注意:消息处理函数是消息自我驱动的,不需要我们的人为干预。比如:当我们打开设备时,系统会自动调用MM_WIM_OPEN,当我们将数据添加到缓冲区,而缓冲区满时,系统会自动调用MM_WIM_DATA,我们所需要做的,就是对该函数编好相应的源代码。
+ n$ P6 i+ b: r. X' Q* {8 C 现在,我们进入正题:如何实现一个录音机。. |6 F: f" _/ [, F+ g
9 l7 N6 B" l" ] ⑴先对WAVEFORMATEX结构体进行赋值,然后为缓冲区分配内存pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);
* X1 Q* g. i# ^0 x$ gpBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);' [1 A' w3 A& m# S! |$ k
对设备头结构体分配内存pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
- G6 F' G/ A4 k$ }5 w0 EpWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));& C( P, h& A8 @, G/ B/ _
然后使用wave音频相关函数对输入数据进行操作:
. l2 T) }2 i) t3 ?) o1 Q+ \4 h2 v+ d3 J$ E: U/ Q) M" l: M
①为波形输入设备准备缓冲区waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));
8 x6 J, [7 T8 p6 ewaveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));
5 O I. K; r& {/ ?8 L. m②为波形输入设备添加缓冲区waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;
; p5 g: @5 P8 C; S) S: c# j: jwaveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;9 ?7 a, i8 q" g" q8 ^$ C) n) D
③启动声音输入设备,将输入数据写入内存waveInStart (hWaveIn) ;⑵编写消息处理函数,其中,MM_WIM_DATA函数是本程序的核心。其主要作用是将输入数据另行保存在一缓冲区内(pSaveBuffer),该缓冲区的长度将随着已录入数据的大小而增加,从而实现保存输入话音数据的功能。同时,可将缓冲区内数据保存为wav文件。其具体实现如下:CFile m_file;! l1 A+ e$ u. Z1 l6 C1 S7 h
CFileException fileException;
m @0 P# l# g8 b( ZCString m_csFileName= "F:\\audio.wav";//保存路径
2 S1 L+ ~9 ^ C" @9 z" ^m_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);5 m" L1 t9 A& z; ?7 T& G: z
DWORD m_WaveHeaderSize = 38;
0 S6 i G; A& @2 L& A3 [DWORD m_WaveFormatSize = 18;2 U& {& G8 |/ S; n {& J3 D
m_file.SeekToBegin();( {1 d. f7 x. ^
m_file.Write("RIFF",4);1 e: Z% F! i3 Z: C
unsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize);
9 ~3 F; z' F bm_file.Write(&Sec,sizeof(Sec));
6 r5 P8 K# X" h- ]m_file.Write("WAVE",4);
* x% ]4 i; T4 u+ f' ?m_file.Write("fmt ",4);6 X8 C: x1 X' P: N
m_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));
) ~$ l4 y+ C Bm_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag));9 `" D; s% |( k* y% w) ]1 {9 d
m_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));5 H% G' W3 W0 m1 k* W9 Y9 }
m_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));
5 j8 Q9 ?. o5 ^( g! ?$ F- j' ^0 zm_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));
! P$ z: M0 e" H) Vm_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));# s" I! Z' \ j8 u5 Q0 i. p5 l
m_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));
# p! g6 Q# e& w _* D$ Am_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));; H D+ f. q6 n! f: J
m_file.Write("data",4);
! V& g" L7 y n8 ym_file.Write(&dwDataLength,sizeof(dwDataLength)); ` u) I5 A. d+ L" N8 s
m_file.Write(pSaveBuffer,dwDataLength);. P1 z, i0 l3 l, }, G+ K
m_file.Seek(dwDataLength,CFile::begin);0 k' w6 c* e3 j- Z
m_file.Close();& ~+ o+ A: e/ P% p, _# t+ `
存在的问题:
- F9 e* ?6 W9 x' V$ W" ?, m我们也才开始vc学习,只是将自己了解的知识与和我们水平相当的新手们分享,希望一起提高。本程序有时不稳定,但音质很好,可能还有尚未暴露的错误,恳请广大高手们不吝赐教。E-mail: xuejing0103@126.com,smilewenjuan@yahoo.com.cn/ @8 n2 G6 G$ o+ e2 D3 S$ u5 |, g
+ c( _& Q8 \) U7 B% C
原文:http://www.vckbase.com/document/viewdoc/?id=1800 |
|