|
作者:任雪景、文娟 ) D) M, h4 D. D4 Q3 C* i
下载源代码
7 s1 h3 }2 f( _0 e
! G6 N7 e7 c. P; E- t& h 引言( q6 E7 \# N8 s. k
我是C++的初学者,入门都要靠VCKBASE,好在里面有很多适合于初学者的例子,让我少走了很多弯路,为了回馈大家,我也把我最近刚完成的一个简单的小程序提供给大家,让那些曾经和我一样徘徊在C++大门之外的人能快些掌握要领,大家不妨看一看。* a# {" }3 n: P9 C
本文以VC知识库第26期 栾义明 先生的《基于API的录音机程序》为基础的,在此深表感谢!相同之处不再重复,我在此基础上增加了将录音保存为wav文件的格式,便于大家参考。
4 V9 ?' E. x5 A x- C基本步骤及思想:设定音频采集参数(采样率、声道等),打开音频设备、准备wave数据头和开辟缓存,操作采集的数据并保存为wav文件。设定音频回放参数,打开回放设备、准备wave数据头和写wave数据。另外样本程序需包含#include <mmsystem.h>和#pragmacomment(lib,"WINMM.LIB")多媒体支持。
: W/ n+ X( d7 E7 D8 {$ p 在介绍程序前,需要你对wave文件的格式和相关一些基础概念有所了解,这些均可以在msdn中查找,为方便理解,我们将其整理,如果对这些基础知识已有所了解,可以跳过。
/ a- p% E+ ~$ f# z; X
4 Z6 k3 Y7 j$ E O, U 概念1、定义波形数据格式6 R- b+ k+ m: M& w6 m/ @ @
typedef struct{WORD wFormatTag; ; L7 c5 e* j9 N5 S
WORD nChannels; % X8 Y) R9 }6 _4 L. }8 w3 x" s5 B5 J3 W
DWORD nSamplesPerSec;
; J) L* F8 Q x) |; f DWORD nAvgBytesPerSec;, g7 r; C1 o- r. w( V* I) e
WORD nBlockAlign; ) E1 p7 l# D% v5 _ O% ~# Y
WORD wBitsPerSample;
% {. t( r9 ~- ^) L7 {- F- V6 _0 D6 f WORD cbSize; } WAVEFORMATEX; 具体参数解释如下:3 |: J) g! K) I
wFormatTag:波形数据的格式,定义在MMREG.H文件中
" Y4 t' k% F5 p, h2 x0 e; ] nChannels:波形数据的通道数:单声道或立体声
/ i* J9 R8 k2 }! { nSamplesPerSec:采样率,对于PCM格式的波形数据,采样率有8.0 kHz,11.025kHz,22.05 kHz,44.1 kHz等
0 p, g* A9 o/ q9 r+ u nAvgBytesPerSec:数据率,对于PCM格式的波形数据,数据率等于采样率乘以每样点字节数2 n. \. ]- b8 b" m* B
nBlockAlign:每个样点字节数
# }# B6 B& L' y9 x. U wBitsPerSample:采样精度,对于PCM格式的波形数据,采样精度为8或163 B$ g) s) f2 s% a# Z3 o7 l
cbSize:附加格式信息的数据块大小
5 S$ }8 \9 N# P! F* F 概念2、定义设备头结构
8 r* t% U0 f' T+ a9 |0 H WAVEHDR定义了指向波形数据缓冲区的设备头。typedef struct { LPSTR lpData;
. Y2 {* f$ B1 W! m8 Z- S+ R DWORD dwBufferLength;
# ]6 Z" C$ f/ N! c DWORD dwBytesRecorded;
7 }3 T6 f- M% D8 C DWORD dwUser; ' a) c& C+ s% K& B4 N; D2 ?
DWORD dwFlags; Y. E7 a( Z' m0 Z; z) W
DWORD dwLoops; 6 {/ L" E6 h, U# h
struct wavehdr_tag * lpNext;
& Y5 A; _3 q3 C+ W( X% R DWORD reserved; } WAVEHDR; lpData:波形数据的缓冲区地址
7 o6 v8 _1 ?! y dwBufferLength:波形数据的缓冲区地址的长度
7 @' ~$ ?/ K" n! T, J dwBytesRecorded:当设备用于录音时,标志已经录入的数据长度
# m9 G" k7 ?5 ]& ? dwUser:用户数据: b1 L+ {& r- O7 E' {
dwFlags:波形数据的缓冲区的属性2 D: {$ v* J3 H" I+ ?
dwLoops:播放循环的次数,仅用于播放控制中4 X' B3 [- I0 B3 P) D C0 O/ P
lpNext和reserved均为保留值$ k+ {: D: a" s' Y; Z$ x; C6 e H
注意:上述结构体以及我们在程序中所使用到的“HWAVEIN””HWAVEOUT”结构体均是系统已经存在的,我们只需要对其进行赋值即可。 d6 k! I: v' y9 a3 M1 k8 O; m3 m
概念3、消息处理函数( D7 J7 S* h0 v% |7 d) A
MM_WIM_OPEN //设备的打开
|8 T6 f# t0 P% L/ @0 u MM_WIM_DATA //设备数据的采集及操作
" `# O6 B, s2 G2 D7 B* M/ v9 h MM_WIM_CLOSE //设备的关闭/ J% ?) Z& q9 ]2 n2 V
相应回放设备的消息分别为MM_WOM_OPEN,MM_WOM_DATA,MM_WOM_CLOSE.
5 Q: V0 K, c8 f+ j+ u$ `- o8 x 注意:消息处理函数是消息自我驱动的,不需要我们的人为干预。比如:当我们打开设备时,系统会自动调用MM_WIM_OPEN,当我们将数据添加到缓冲区,而缓冲区满时,系统会自动调用MM_WIM_DATA,我们所需要做的,就是对该函数编好相应的源代码。! r) i& o( u% j0 E: ^8 J5 k
现在,我们进入正题:如何实现一个录音机。
, g' ]; x+ s/ P, x2 A( x/ n ; R, F8 ~! h3 O! O& r
⑴先对WAVEFORMATEX结构体进行赋值,然后为缓冲区分配内存pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);
9 p: f3 R6 T3 x3 \6 P! e6 \pBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);
2 H* E# ]; K# @) D, P4 O对设备头结构体分配内存pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));1 U, Y7 [8 `( F
pWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
1 {+ @+ b3 e. v% {然后使用wave音频相关函数对输入数据进行操作:
; ~ q# ^. ^, r
# b; f3 q" l! j2 J: c. y①为波形输入设备准备缓冲区waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));
- C) [3 d$ i6 K! g. w- |& vwaveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));
- D2 ]" n- n6 f7 r& ~! _7 T9 j4 v2 j$ J②为波形输入设备添加缓冲区waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;8 |0 `" a2 ]4 h! c1 T* }" Q
waveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;# R# T7 C( d" u: Z; Z2 K: _/ D. R
③启动声音输入设备,将输入数据写入内存waveInStart (hWaveIn) ;⑵编写消息处理函数,其中,MM_WIM_DATA函数是本程序的核心。其主要作用是将输入数据另行保存在一缓冲区内(pSaveBuffer),该缓冲区的长度将随着已录入数据的大小而增加,从而实现保存输入话音数据的功能。同时,可将缓冲区内数据保存为wav文件。其具体实现如下:CFile m_file;7 Z4 n" _* @9 S F( ?7 v7 c
CFileException fileException;
& t. T" L+ G- ~; i, ]5 b: x ICString m_csFileName= "F:\\audio.wav";//保存路径& n& b: k$ O& P+ b) w/ ]. j
m_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);0 v1 @* \4 s# m/ y+ }5 E% a! I9 N+ P
DWORD m_WaveHeaderSize = 38;2 t9 y+ l$ k/ s7 U( S
DWORD m_WaveFormatSize = 18;
0 x/ {9 e+ l$ t1 U- Cm_file.SeekToBegin();4 L U2 \. o1 P* w T [' U
m_file.Write("RIFF",4);& }! Q) R% L9 l$ [- J& ~
unsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize);9 R2 Q& c# i; u
m_file.Write(&Sec,sizeof(Sec));
: \# V+ g0 M2 ?m_file.Write("WAVE",4);
: a `& P2 ~2 dm_file.Write("fmt ",4);
/ G) F$ a" }) h& M- d. A/ vm_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));! u5 ^ @8 t% I8 {% H
m_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag));( q: s& k p2 w; l$ P
m_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));
7 \; ~. r0 C( k5 p4 N- tm_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));
5 o# L/ J/ c& I& V3 R1 ~* q( N3 K4 rm_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));
z& K. B0 o# b) k u( G% Y) T8 dm_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));. Z, K) Z7 ]) |$ R
m_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));% }) B" _7 e( Q
m_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));
1 m+ Y8 D" T# X+ [! lm_file.Write("data",4);7 d6 B# m& L" l( ]
m_file.Write(&dwDataLength,sizeof(dwDataLength));/ y' T1 O- N- D9 ?- p* W
m_file.Write(pSaveBuffer,dwDataLength);, g ]; v" z+ d, k& E D9 H
m_file.Seek(dwDataLength,CFile::begin);
; M; X( N" g- ~% _( ?6 dm_file.Close();. ]. h I5 l& D: J
存在的问题:
! E5 o: k) P( k2 a) u* K3 Z6 F我们也才开始vc学习,只是将自己了解的知识与和我们水平相当的新手们分享,希望一起提高。本程序有时不稳定,但音质很好,可能还有尚未暴露的错误,恳请广大高手们不吝赐教。E-mail: xuejing0103@126.com,smilewenjuan@yahoo.com.cn9 g3 D2 m* `$ K9 b5 A
( q' y9 P7 f1 ]- u: `3 ]/ Q8 ?原文:http://www.vckbase.com/document/viewdoc/?id=1800 |
|