|
|
|
作者:任雪景、文娟
% v j, t1 H# \: h6 D# w7 ?' q- a 下载源代码# Y2 M- G0 \) k$ i
7 v3 g* }8 l3 X1 q1 K) ?* H! q
引言$ |/ {; h# h( ^0 R
我是C++的初学者,入门都要靠VCKBASE,好在里面有很多适合于初学者的例子,让我少走了很多弯路,为了回馈大家,我也把我最近刚完成的一个简单的小程序提供给大家,让那些曾经和我一样徘徊在C++大门之外的人能快些掌握要领,大家不妨看一看。
8 {: p1 n c* F6 K7 }, w 本文以VC知识库第26期 栾义明 先生的《基于API的录音机程序》为基础的,在此深表感谢!相同之处不再重复,我在此基础上增加了将录音保存为wav文件的格式,便于大家参考。
& h8 I- C5 Q: {, F; {基本步骤及思想:设定音频采集参数(采样率、声道等),打开音频设备、准备wave数据头和开辟缓存,操作采集的数据并保存为wav文件。设定音频回放参数,打开回放设备、准备wave数据头和写wave数据。另外样本程序需包含#include <mmsystem.h>和#pragmacomment(lib,"WINMM.LIB")多媒体支持。# _* ]0 Y4 ~, D
在介绍程序前,需要你对wave文件的格式和相关一些基础概念有所了解,这些均可以在msdn中查找,为方便理解,我们将其整理,如果对这些基础知识已有所了解,可以跳过。
. p6 P' \+ @0 Y
: q- w8 Y9 O9 X5 l+ s 概念1、定义波形数据格式
; L( V! ^1 K6 G+ R& H! I. W typedef struct{WORD wFormatTag; " z" [# {5 Q% Z; [! S
WORD nChannels; o8 |. O b9 g# q
DWORD nSamplesPerSec; ; c% }' f% r/ H B" u1 d3 B
DWORD nAvgBytesPerSec;
, Z) i# \1 P9 A, K5 ]( x8 n1 o WORD nBlockAlign; 6 v, l* j; ~: {) Q7 |
WORD wBitsPerSample;
( O- Y D7 Q) y6 F+ } WORD cbSize; } WAVEFORMATEX; 具体参数解释如下:
6 W; n0 f; n G% ^( g( P" e wFormatTag:波形数据的格式,定义在MMREG.H文件中# g/ D- [8 J4 t
nChannels:波形数据的通道数:单声道或立体声. m& [1 h, F. f$ k; v! w
nSamplesPerSec:采样率,对于PCM格式的波形数据,采样率有8.0 kHz,11.025kHz,22.05 kHz,44.1 kHz等
6 x# m/ t" W( O) n9 s" Z nAvgBytesPerSec:数据率,对于PCM格式的波形数据,数据率等于采样率乘以每样点字节数# i$ z. f. [! p/ A
nBlockAlign:每个样点字节数
+ a5 A7 Z9 u n. C+ T1 ] wBitsPerSample:采样精度,对于PCM格式的波形数据,采样精度为8或16
. _. L1 w) |( A6 V8 m0 ] cbSize:附加格式信息的数据块大小! V% {/ t2 {9 e% t4 T$ L& p) C8 W
概念2、定义设备头结构
4 {9 W3 U" i+ q8 I7 r$ {1 R7 o WAVEHDR定义了指向波形数据缓冲区的设备头。typedef struct { LPSTR lpData;
5 w9 w" W/ J( [' P2 k% r! ~ DWORD dwBufferLength; 0 a; p; x. y& y" b6 K, F/ ^
DWORD dwBytesRecorded; 8 X' `2 V- A1 R3 A
DWORD dwUser;
! H; I9 ^# q7 A DWORD dwFlags; / }1 m( |6 {+ N8 I1 v: F8 h
DWORD dwLoops; , |: T' ^4 n' N. U( D7 ~! w( C
struct wavehdr_tag * lpNext; . r7 m. Q! n' z; ~- S$ U1 M
DWORD reserved; } WAVEHDR; lpData:波形数据的缓冲区地址
# a: M3 ^3 l4 n& n* d* P dwBufferLength:波形数据的缓冲区地址的长度+ F G' {; F6 A# n# A# S
dwBytesRecorded:当设备用于录音时,标志已经录入的数据长度8 I' F$ ?- C5 y! k y! b. Q- `
dwUser:用户数据
* ]" U- W% r( J dwFlags:波形数据的缓冲区的属性* q9 j/ [! b. B3 L3 F- J4 H2 W
dwLoops:播放循环的次数,仅用于播放控制中
; N/ K& z/ W9 I z( H lpNext和reserved均为保留值* z5 _& v/ {" W# S) F! T+ B
注意:上述结构体以及我们在程序中所使用到的“HWAVEIN””HWAVEOUT”结构体均是系统已经存在的,我们只需要对其进行赋值即可。
[# C, o v& R; k& O 概念3、消息处理函数( o5 W* \; _- ^9 K0 c) a3 u
MM_WIM_OPEN //设备的打开
8 V* j5 K0 B+ H2 W+ r' Z MM_WIM_DATA //设备数据的采集及操作
( F/ `' o1 R" O; ~, s" k3 ?6 b MM_WIM_CLOSE //设备的关闭
; K/ Z# ]0 n" ^% B# `3 I 相应回放设备的消息分别为MM_WOM_OPEN,MM_WOM_DATA,MM_WOM_CLOSE.
/ A/ m( h( x4 E. |5 |# u0 ` 注意:消息处理函数是消息自我驱动的,不需要我们的人为干预。比如:当我们打开设备时,系统会自动调用MM_WIM_OPEN,当我们将数据添加到缓冲区,而缓冲区满时,系统会自动调用MM_WIM_DATA,我们所需要做的,就是对该函数编好相应的源代码。 ?2 b# v% J8 n& R( A# s4 U! c% Z% ^
现在,我们进入正题:如何实现一个录音机。4 g+ z: Y' g6 o+ h" c4 }
: u$ d5 ?+ n8 F: [* N+ j- P7 E
⑴先对WAVEFORMATEX结构体进行赋值,然后为缓冲区分配内存pBuffer1=(PBYTE)malloc(INP_BUFFER_SIZE);
- r' ^& g0 F+ e$ i6 DpBuffer2=(PBYTE)malloc(INP_BUFFER_SIZE);; ]& l) L( Z6 G$ X% ]1 z i- c
对设备头结构体分配内存pWaveHdr1=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
8 _5 z) @: N% `& YpWaveHdr2=reinterpret_cast<PWAVEHDR>(malloc(sizeof(WAVEHDR)));
3 {* f/ t( q2 D然后使用wave音频相关函数对输入数据进行操作:- R/ C; d2 C9 R# }" \
; I6 Z9 X) `9 i7 w8 N2 X( W0 x2 w2 j
①为波形输入设备准备缓冲区waveInPrepareHeader(hWaveIn,pWaveHdr1,sizeof(WAVEHDR));
3 X5 X9 n) F( }) n" u* C# qwaveInPrepareHeader(hWaveIn,pWaveHdr2,sizeof(WAVEHDR));
( S; n+ \& N. m2 g6 }1 _! i5 W$ m②为波形输入设备添加缓冲区waveInAddBuffer (hWaveIn, pWaveHdr1, sizeof (WAVEHDR)) ;
0 |; O. ]9 N- G5 Z0 f0 m) GwaveInAddBuffer (hWaveIn, pWaveHdr2, sizeof (WAVEHDR)) ;
$ e1 E2 r5 i' E6 E4 L V$ Q③启动声音输入设备,将输入数据写入内存waveInStart (hWaveIn) ;⑵编写消息处理函数,其中,MM_WIM_DATA函数是本程序的核心。其主要作用是将输入数据另行保存在一缓冲区内(pSaveBuffer),该缓冲区的长度将随着已录入数据的大小而增加,从而实现保存输入话音数据的功能。同时,可将缓冲区内数据保存为wav文件。其具体实现如下:CFile m_file;
2 B; }6 s+ p( h; g7 D+ CCFileException fileException;0 y3 |3 c) q; M. Q7 }0 d- w* U
CString m_csFileName= "F:\\audio.wav";//保存路径
& {0 M. o" ?2 w3 f4 w; m+ `m_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException);1 H$ N) C5 v( P5 ?) A
DWORD m_WaveHeaderSize = 38;. R, x q# {1 q3 R& r8 x& M; J4 E
DWORD m_WaveFormatSize = 18;
7 U" I( T0 Y5 d% D- wm_file.SeekToBegin();" l( n$ j6 e i
m_file.Write("RIFF",4);- K& g a- Z& o
unsigned int Sec=(sizeof pSaveBuffer + m_WaveHeaderSize);# a& x* ^6 V: ?" v b, J
m_file.Write(&Sec,sizeof(Sec));
" V. N6 h0 b- k) |* u, Z& Km_file.Write("WAVE",4);
+ v, e |( i. j4 @/ l; }9 n# V: nm_file.Write("fmt ",4);' Z0 Q/ D" i" p& f7 ], |
m_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize));; l" ]% }7 _# p* F, g
m_file.Write(&waveform.wFormatTag,sizeof(waveform.wFormatTag));
7 X1 x1 Q! M. O5 }0 @$ V5 lm_file.Write(&waveform.nChannels,sizeof(waveform.nChannels));
* r' R) R, O9 h+ t9 qm_file.Write(&waveform.nSamplesPerSec,sizeof(waveform.nSamplesPerSec));8 M" i9 O7 ~' D& x* Y4 `. S. Q
m_file.Write(&waveform.nAvgBytesPerSec,sizeof(waveform.nAvgBytesPerSec));
' {# o0 {9 R# B% nm_file.Write(&waveform.nBlockAlign,sizeof(waveform.nBlockAlign));9 `8 C' h6 G2 m
m_file.Write(&waveform.wBitsPerSample,sizeof(waveform.wBitsPerSample));
0 h. |1 ~& @( q; h& mm_file.Write(&waveform.cbSize,sizeof(waveform.cbSize));
% h( \! P9 y& z+ @- Rm_file.Write("data",4);6 P% H7 l6 v/ V. [" S! |4 y$ w! d
m_file.Write(&dwDataLength,sizeof(dwDataLength));
4 M9 T. s: C3 ?, f7 p4 Jm_file.Write(pSaveBuffer,dwDataLength);: _# Q6 }3 a" `% z9 z
m_file.Seek(dwDataLength,CFile::begin);
& s# u$ z+ K( f6 z" N8 @5 Rm_file.Close();
/ W- L0 ~$ \2 W+ ]存在的问题:) S- Q0 B; _/ K9 G$ h; o* H
我们也才开始vc学习,只是将自己了解的知识与和我们水平相当的新手们分享,希望一起提高。本程序有时不稳定,但音质很好,可能还有尚未暴露的错误,恳请广大高手们不吝赐教。E-mail: xuejing0103@126.com,smilewenjuan@yahoo.com.cn
, k0 a+ }* ]7 j* w2 r# w" V3 R' }- G. x: U7 K* r4 C3 W- {
原文:http://www.vckbase.com/document/viewdoc/?id=1800 |
|