|
|
|
作者:任雪景、文娟 % J. p9 `0 O ]+ O6 h- J
下载源代码
0 D- M3 m. o8 v& y% o ( _. G+ J y' O
引言
* c8 E9 E& p2 i5 k Y 我是C++的初学者,入门都要靠VCKBASE,好在里面有很多适合于初学者的例子,让我少走了很多弯路,为了回馈大家,我也把我最近刚完成的一个简单的小程序提供给大家,让那些曾经和我一样徘徊在C++大门之外的人能快些掌握要领,大家不妨看一看。
' Y+ r0 O u) A) a" t; z, m9 B J/ ] 本文以VC知识库第26期 栾义明 先生的《基于API的录音机程序》为基础的,在此深表感谢!相同之处不再重复,我在此基础上增加了将录音保存为wav文件的格式,便于大家参考。 q! | a, c: ^, t8 j
基本步骤及思想:设定音频采集参数(采样率、声道等),打开音频设备、准备wave数据头和开辟缓存,操作采集的数据并保存为wav文件。设定音频回放参数,打开回放设备、准备wave数据头和写wave数据。另外样本程序需包含#include <mmsystem.h>和#pragmacomment(lib,"WINMM.LIB")多媒体支持。! w6 e0 W0 A% b& n2 w2 Z+ h
在介绍程序前,需要你对wave文件的格式和相关一些基础概念有所了解,这些均可以在msdn中查找,为方便理解,我们将其整理,如果对这些基础知识已有所了解,可以跳过。8 {8 w7 \1 k# `* I% Z, `
6 u! O4 ~ Y2 ]. V5 E" n* l 概念1、定义波形数据格式4 {% r( B3 V' d% s) c" n- Q9 R
typedef struct{WORD wFormatTag;
; B+ G6 Y" L8 G# N1 ^( e) G) Q) O WORD nChannels;
' |" M( }! h1 g6 Q8 g DWORD nSamplesPerSec;
4 ?* ?3 Z+ }4 O DWORD nAvgBytesPerSec;, D% X2 [; u8 j3 L% A! i5 `0 [
WORD nBlockAlign;
) W6 V$ G4 f: V* u# \" G! W' z# Y WORD wBitsPerSample;
5 }9 A* n3 }6 n' z/ i6 P WORD cbSize; } WAVEFORMATEX; 具体参数解释如下:* i8 z O8 w* p
wFormatTag:波形数据的格式,定义在MMREG.H文件中
4 ~0 Z4 Y% v9 c C) T/ I" V6 n3 Z nChannels:波形数据的通道数:单声道或立体声
% L# P0 ]1 i; c( @/ v0 |( a nSamplesPerSec:采样率,对于PCM格式的波形数据,采样率有8.0 kHz,11.025kHz,22.05 kHz,44.1 kHz等9 M2 u- j; R* c: A+ C
nAvgBytesPerSec:数据率,对于PCM格式的波形数据,数据率等于采样率乘以每样点字节数
: F0 B% ^- f) ?6 ?3 J4 j, a0 m6 ]6 N nBlockAlign:每个样点字节数) K4 ?' q% ?/ t# R
wBitsPerSample:采样精度,对于PCM格式的波形数据,采样精度为8或16
- G, ~. M0 Y2 [: [5 C9 V0 Y cbSize:附加格式信息的数据块大小7 H# G/ n* X5 F4 s. m6 z# i% Q
概念2、定义设备头结构
1 s- x, G' S& \( U WAVEHDR定义了指向波形数据缓冲区的设备头。typedef struct { LPSTR lpData;
3 y' P5 V5 W9 }$ U DWORD dwBufferLength;
# N" x- @& [. N, G0 m! t9 w DWORD dwBytesRecorded;
5 J4 m7 h* q) d% c& e9 l I2 M DWORD dwUser; 4 ]% U& t5 D3 Y1 B8 ~
DWORD dwFlags;
9 @7 o: [! Z. E! d9 q7 g* J DWORD dwLoops; 9 A, s: q" _8 [: }. H
struct wavehdr_tag * lpNext;
7 j' |- W; P& P5 H DWORD reserved; } WAVEHDR; lpData:波形数据的缓冲区地址
. E t5 D" h, I3 v dwBufferLength:波形数据的缓冲区地址的长度
2 H/ }% A# z' Y& y. F4 Y6 O' l3 ^ dwBytesRecorded:当设备用于录音时,标志已经录入的数据长度% b& n% J& I6 |( @$ {
dwUser:用户数据
0 O& _" B O+ s& V dwFlags:波形数据的缓冲区的属性% X) c2 A# i% M
dwLoops:播放循环的次数,仅用于播放控制中
) g, x0 h- C' y lpNext和reserved均为保留值
- T- B0 N: ~7 e6 h, J# ~0 n 注意:上述结构体以及我们在程序中所使用到的“HWAVEIN””HWAVEOUT”结构体均是系统已经存在的,我们只需要对其进行赋值即可。) d5 B1 X$ c' V
概念3、消息处理函数+ p5 Q% A1 `9 |; q, Z
MM_WIM_OPEN //设备的打开
( A" N" d+ b! A5 ^+ W: j* ?% } MM_WIM_DATA //设备数据的采集及操作
# M" B, H+ f3 } MM_WIM_CLOSE //设备的关闭
( d7 L5 {' y( t: `% p) y 相应回放设备的消息分别为MM_WOM_OPEN,MM_WOM_DATA,MM_WOM_CLOSE./ p9 V+ k, k- J0 ?, @' _
注意:消息处理函数是消息自我驱动的,不需要我们的人为干预。比如:当我们打开设备时,系统会自动调用MM_WIM_OPEN,当我们将数据添加到缓冲区,而缓冲区满时,系统会自动调用MM_WIM_DATA,我们所需要做的,就是对该函数编好相应的源代码。
$ d. F, Q, |2 t7 H8 i 现在,我们进入正题:如何实现一个录音机。# K' n; z7 [. t5 w% Q; c1 G
7 F7 n1 q+ `1 p) J6 h! z0 h$ B9 F ⑴先对WAVEFORMATEX结构体进行赋值,然后为缓冲区分配内存pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);
) l9 n) e& J4 ~% K. w) r$ xpBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);! Z. o! A! n+ Q2 m5 x& u# v
对设备头结构体分配内存pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
+ U0 X& `- q( |# O% NpWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
& }' \9 k. B( s6 W/ W3 L然后使用wave音频相关函数对输入数据进行操作:
& g- o. {, G: g* L0 ?$ j
# o2 f. E! e' B9 {: D) o①为波形输入设备准备缓冲区waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));9 w: W% ^" s1 a* h; n
waveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));
, y) |/ K& Q& @8 H1 E7 j3 e②为波形输入设备添加缓冲区waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;
, M# l& a+ i, [: V# f4 IwaveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;
; t% R: b5 I; r+ J3 `* R$ h* p③启动声音输入设备,将输入数据写入内存waveInStart (hWaveIn) ;⑵编写消息处理函数,其中,MM_WIM_DATA函数是本程序的核心。其主要作用是将输入数据另行保存在一缓冲区内(pSaveBuffer),该缓冲区的长度将随着已录入数据的大小而增加,从而实现保存输入话音数据的功能。同时,可将缓冲区内数据保存为wav文件。其具体实现如下:CFile m_file;
" X Y0 v. k+ F$ ]$ |CFileException fileException;
n( ^0 U* f2 |# ^! T5 C, UCString m_csFileName= "F:\\audio.wav";//保存路径
9 U1 A6 E! W+ ?# \m_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);
' o* p6 h5 C z' Q3 `+ RDWORD m_WaveHeaderSize = 38;
6 d: E8 G4 \& \- x' n* WDWORD m_WaveFormatSize = 18;
& q* Z; G3 |5 Am_file.SeekToBegin();
. M. f+ n3 H& Z. B+ x$ M, N1 c& ym_file.Write("RIFF",4);
2 n4 A) p' Z' `) a7 I5 u' ]) E% n6 ~unsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize);
3 s0 m. N. ]5 N& j7 X9 r' Sm_file.Write(&Sec,sizeof(Sec));# g+ w, V; P( V V- H: f6 j) {
m_file.Write("WAVE",4);
6 @ a& k) m9 h/ Y2 ym_file.Write("fmt ",4);" P5 d f" D8 }" ~1 K: Z+ q0 T d
m_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));
3 L: H% S; o# _' S0 s. A5 _m_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag));
j: k+ j N0 b& s1 |m_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));4 C( b2 [: Z8 w5 S) I: k6 y
m_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));( v6 L, `5 Y* O7 y" O, k, l6 p
m_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));1 I3 P8 S- E* I
m_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));' u0 e' w7 E7 e& d5 k
m_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));
* U e9 D6 `; g- i4 v2 \* hm_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));7 O- i* U* y- c* ~5 j
m_file.Write("data",4);
3 d7 [1 ^% B8 }* z. T9 vm_file.Write(&dwDataLength,sizeof(dwDataLength));
! A& ]2 u) @3 Q5 j! ?. s6 k6 gm_file.Write(pSaveBuffer,dwDataLength);5 I# D# z! r7 a
m_file.Seek(dwDataLength,CFile::begin);% ~& Y( y. C# W3 P1 P) L7 n
m_file.Close();( |& s# a/ u2 X
存在的问题:
* ], M4 j C5 `3 L我们也才开始vc学习,只是将自己了解的知识与和我们水平相当的新手们分享,希望一起提高。本程序有时不稳定,但音质很好,可能还有尚未暴露的错误,恳请广大高手们不吝赐教。E-mail: xuejing0103@126.com,smilewenjuan@yahoo.com.cn
a* s! A' C: m& O% W9 e7 o9 ^9 X
( \( o& \) J7 ?1 _原文:http://www.vckbase.com/document/viewdoc/?id=1800 |
|