|
|
|
作者:任雪景、文娟
3 L4 x8 {/ z B. V$ S 下载源代码
: z+ a( o$ S8 U3 `, U
0 v% l/ _# F) T9 N: t; l7 ~# p 引言0 O+ ]8 W6 l. i/ Z* a7 k
我是C++的初学者,入门都要靠VCKBASE,好在里面有很多适合于初学者的例子,让我少走了很多弯路,为了回馈大家,我也把我最近刚完成的一个简单的小程序提供给大家,让那些曾经和我一样徘徊在C++大门之外的人能快些掌握要领,大家不妨看一看。/ F* k1 l0 o* [2 {
本文以VC知识库第26期 栾义明 先生的《基于API的录音机程序》为基础的,在此深表感谢!相同之处不再重复,我在此基础上增加了将录音保存为wav文件的格式,便于大家参考。6 y7 R& t# f! p. t& X
基本步骤及思想:设定音频采集参数(采样率、声道等),打开音频设备、准备wave数据头和开辟缓存,操作采集的数据并保存为wav文件。设定音频回放参数,打开回放设备、准备wave数据头和写wave数据。另外样本程序需包含#include <mmsystem.h>和#pragmacomment(lib,"WINMM.LIB")多媒体支持。; l8 B0 I1 b6 m! ?# S- R
在介绍程序前,需要你对wave文件的格式和相关一些基础概念有所了解,这些均可以在msdn中查找,为方便理解,我们将其整理,如果对这些基础知识已有所了解,可以跳过。
$ @% m7 Y f. P' f) p# _4 V" V y$ j
* Y" e5 x: Z) \ 概念1、定义波形数据格式1 T+ e4 Q$ O, I; B- s p6 J' W+ w
typedef struct{WORD wFormatTag; 7 Z2 R, }; t, C5 ^4 J& B' d
WORD nChannels; 3 B# |2 p$ ~7 w0 m U8 b6 i
DWORD nSamplesPerSec; . y# M( I0 r/ o8 C( A8 l& m
DWORD nAvgBytesPerSec;
. p6 R. B6 G$ Z WORD nBlockAlign; . T( q/ k+ z. j! z. m V
WORD wBitsPerSample; - U! o7 Q1 R* G; @. A
WORD cbSize; } WAVEFORMATEX; 具体参数解释如下:
/ t) J2 T3 I- R. ?& X' \ wFormatTag:波形数据的格式,定义在MMREG.H文件中
) z: }( H. i3 h' C9 x0 N nChannels:波形数据的通道数:单声道或立体声
5 c, A. i" Z) F1 |) p nSamplesPerSec:采样率,对于PCM格式的波形数据,采样率有8.0 kHz,11.025kHz,22.05 kHz,44.1 kHz等
: M$ L4 ]$ X+ t& Z nAvgBytesPerSec:数据率,对于PCM格式的波形数据,数据率等于采样率乘以每样点字节数3 k! A- L3 A0 e' f6 D: i9 n
nBlockAlign:每个样点字节数
6 [( ]1 o1 a' I8 V5 ]; s. G wBitsPerSample:采样精度,对于PCM格式的波形数据,采样精度为8或16
$ l8 M4 T1 i# a$ n cbSize:附加格式信息的数据块大小
9 ], i V7 T4 F, K& N0 P! o* q 概念2、定义设备头结构0 T" q5 C* {+ W% F
WAVEHDR定义了指向波形数据缓冲区的设备头。typedef struct { LPSTR lpData; + Y. @* k7 G6 A; N8 C: Z* M2 Y
DWORD dwBufferLength;
* S7 Q. ~3 y3 t DWORD dwBytesRecorded;
2 @& Z1 t5 E# U+ d5 |6 L9 ?2 ]' O DWORD dwUser;
" r! t @6 Y% E1 U9 I' z: } DWORD dwFlags; * H4 s& o( u+ s7 X
DWORD dwLoops; : x+ v7 x# ^; U/ R f7 v
struct wavehdr_tag * lpNext; * \" {. {: Y/ V7 `
DWORD reserved; } WAVEHDR; lpData:波形数据的缓冲区地址
1 O2 U( B8 m( U dwBufferLength:波形数据的缓冲区地址的长度: w1 [2 ]& H' W. v, C3 b
dwBytesRecorded:当设备用于录音时,标志已经录入的数据长度
9 K7 B6 W, P2 X. u! n3 p dwUser:用户数据
" C1 `( N* y/ R dwFlags:波形数据的缓冲区的属性
' J8 d9 H# { a, A2 U( g dwLoops:播放循环的次数,仅用于播放控制中0 ?' _* x4 X6 d5 o' b
lpNext和reserved均为保留值
0 {# v2 H5 d4 a7 q* V 注意:上述结构体以及我们在程序中所使用到的“HWAVEIN””HWAVEOUT”结构体均是系统已经存在的,我们只需要对其进行赋值即可。# O: I& P- E% M9 C Q
概念3、消息处理函数' g& i' `6 v' i) Z5 J( ~, A
MM_WIM_OPEN //设备的打开
& \ ]! V0 R. t9 U, _0 R! q MM_WIM_DATA //设备数据的采集及操作9 n+ ]2 x% U# ^% }! a
MM_WIM_CLOSE //设备的关闭
& u. x2 X7 d# g/ C 相应回放设备的消息分别为MM_WOM_OPEN,MM_WOM_DATA,MM_WOM_CLOSE.
) n& o! d+ m* b5 Z! u4 W9 I( r J 注意:消息处理函数是消息自我驱动的,不需要我们的人为干预。比如:当我们打开设备时,系统会自动调用MM_WIM_OPEN,当我们将数据添加到缓冲区,而缓冲区满时,系统会自动调用MM_WIM_DATA,我们所需要做的,就是对该函数编好相应的源代码。) x$ I1 a, q7 n0 R
现在,我们进入正题:如何实现一个录音机。9 x) X& A9 P. \9 Y I2 G0 h
4 D. r5 ~2 Q6 I5 N$ l t% g
⑴先对WAVEFORMATEX结构体进行赋值,然后为缓冲区分配内存pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);: Y% {7 g8 a: r+ e
pBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);, L' @9 [8 t( a+ l7 m4 w
对设备头结构体分配内存pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
8 _3 b3 c+ R6 j1 ^3 gpWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));" {4 ^+ H( p0 W% r8 e
然后使用wave音频相关函数对输入数据进行操作:
6 Q0 J! x# G; h7 \% i$ c H$ e/ ~* |' e/ F6 |
①为波形输入设备准备缓冲区waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));" X9 B8 Y+ Q4 v1 E; r" g7 j0 @
waveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));
/ w3 I9 d# \9 ^' z9 O9 R3 y" J②为波形输入设备添加缓冲区waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;0 e4 o, u0 u0 k3 c
waveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;' U8 E: j* q- h) @% {
③启动声音输入设备,将输入数据写入内存waveInStart (hWaveIn) ;⑵编写消息处理函数,其中,MM_WIM_DATA函数是本程序的核心。其主要作用是将输入数据另行保存在一缓冲区内(pSaveBuffer),该缓冲区的长度将随着已录入数据的大小而增加,从而实现保存输入话音数据的功能。同时,可将缓冲区内数据保存为wav文件。其具体实现如下:CFile m_file;; y8 }) E: d8 u: S4 S; C
CFileException fileException;- W1 j! c. d1 v* f6 p5 [
CString m_csFileName= "F:\\audio.wav";//保存路径
6 O4 k2 n3 L4 U( ?- [# A4 V9 Em_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);: ?, s. b& \" p f
DWORD m_WaveHeaderSize = 38;
7 q& a7 p: n/ U8 c- }5 XDWORD m_WaveFormatSize = 18;- |3 N+ H0 d$ S t
m_file.SeekToBegin();! z4 }8 U8 V) T7 Q& o+ i2 q
m_file.Write("RIFF",4);7 {7 S- c" W7 l- q
unsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize);8 C& H/ c2 S5 [- a
m_file.Write(&Sec,sizeof(Sec));
; ]0 W% z+ F+ |( e }# zm_file.Write("WAVE",4);
0 M3 c/ S9 I3 a6 r- n% A. Im_file.Write("fmt ",4);) I% q$ ]$ U4 D
m_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));0 s7 \8 i' f- ^8 g2 i
m_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag)); ^/ d) L0 u, c1 t( o/ M q
m_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));" B5 T) o. c3 _5 d, ~; s$ k3 |
m_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));0 _$ X0 q- M) q) V/ k V
m_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));( } H5 t$ u9 X r, T
m_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));1 Z" V: i: I: X( g4 {2 J
m_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));
) x E; _' d9 y6 W1 e7 Im_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));/ g) k% k# R. P A% {2 |' n
m_file.Write("data",4);
+ ~1 H8 S' Y6 L3 R$ L( o1 v) Bm_file.Write(&dwDataLength,sizeof(dwDataLength));2 ?3 c, I3 J' I8 Z1 d
m_file.Write(pSaveBuffer,dwDataLength);
2 }* [& R. K" D$ P& \$ Lm_file.Seek(dwDataLength,CFile::begin);! K" \7 X$ i. O& @5 P9 k ?
m_file.Close();2 l5 Y ~3 H; A% L8 x4 ]
存在的问题:
: c, J5 m# A. B( s我们也才开始vc学习,只是将自己了解的知识与和我们水平相当的新手们分享,希望一起提高。本程序有时不稳定,但音质很好,可能还有尚未暴露的错误,恳请广大高手们不吝赐教。E-mail: xuejing0103@126.com,smilewenjuan@yahoo.com.cn7 h' `( [& X; r# m1 P
4 i+ B# K+ m$ h ~, X* O. O原文:http://www.vckbase.com/document/viewdoc/?id=1800 |
|