多媒体文件I/O
多媒体文件I/O与普通文件I/O相似,但支持多媒体“RIFF”格式,并提供了缓冲和非缓冲文件I/O。
所有的多媒体文件I/O函数名前缀为mmio,消息名前缀为MMIO。
低级波形音频函数:
低级音频服务允许用户直接与音频设备驱动程序打交道,直接控制音频设备如波形,Midi的播放与记录,低级音频函数是一个设备无关接口。
低级音频函数前缀均为wave,按输入函数、输出函数区分为WaveIn××××和WaveOut××××。
波形音频的重放过程。
首先,我们要调用多媒体文件I/O函数mmIO××××( ),并根据多媒体文件I/O生成在wave重放中需要的结构和数据,并将这些结构和数据用waveOut××××( )函数重放。 用户可以根据加密的需要将wave文件篡改,去掉文件头和“fmt”块,只保留数据块。 或者将其压缩,只要重放时能在内存中还原出数据文件。并记得文件音频格式和大小,就能重放音频。
常用mmio函数及实现过程简介:
mmioOpen( ) 打开一个RIFF文件
mmioDescend( ) 进入块
mmioRead( ); 该取RIFF文件
mmioAscend ( ); 跳出块
mmioClose( ); 关闭PIFF文件
对于块来说,进入块和跳出块是配对的。
读取WAV文件的读取过程:
mmioOpen( ) 打开文件
↓
mmioDescend ("WAVE") 进入"fmt"块
↓
mmioRead( ) 读取WAVE文件格式信息
↓
mmioAscend ( ) 跳出"fmt"块
↓
mmioDescend ("data") 进入"data"块
↓
mmioRead( ) 读取WAVE数据信息
↓
mmioClose( ) 关闭文件。
输出WAV文件的过程:
WaveOutOpen ()打开一个输出设备
↓
WaveOutPrepareHeader()准备WAVE数据头。
↓
WaveOutWrite() 将数据写入设备并开始播放
↓
WaveOutReset() 停止播放并重置管理器
↓
WaveOutClose()并闭播放设备
↓
WaveOutUnpareHeader()清理用WaveOutPrepareHeader准备的Wave。
实例:
这个实例实现的功能是首先打开背景音乐,然后每五秒钟加入一段配音。背景音乐放完后将停止播放,大家可以听一下背景+配音+配音+……+配音产生的实际效果。为了实现这个功能,我们封装了一个类。大家可以在光盘上找到这两个文件“wavemix..h”和“wavemix.cpp” .
首先,我们看一下“wavmix.h”,这里定义了一个称为“mwave”的类,
其中成员函数有:构造、析构函数、open(打开文件)、play(播放文件)、Add(往缓冲中加混音文件)、Stop(停止播出)、close(关闭输出设备,类重新初始化)。
现在我们照前面方法建一个基于对话框的程序,在“File View”中加入上述两个文件。打开“WavemixDlg.h”,在前面加上“#include "wavemix.h"”,
在类class cWavemixDlg中加入私有数据“MWAVE mWave”,在类“cwavemixDlg”中加入成员OnlnitDialog( )和OnTime(),,
在此函数中加入一个定时器,打开并播放背景音乐。在定时器的向应过程OnTime()中加入函数mwave.Add("2.waw".)使之每五秒钟向缓冲加入一个配音。编译并运行之。
源程序分析
// wavemix.h : main header file for the WAVEMIX application
//
#if !defined(AFX_WAVEMIX_H__54F4AF66_CE37_11D1_94F8_0000B431BBA1__INCLUDED_)
#define AFX_WAVEMIX_H__54F4AF66_CE37_11D1_94F8_0000B431BBA1__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif
#include "resource.h"// main symbols
/////////////////////////////////////////////
#include "windows.h"
#include "mmsystem.h"
#ifdef WIN32
#define WAVDATABYTE
#else
#define WAVDATABYTE _huge
#define WAVEFORMATEX PCMWAVEFORMAT
#endif
#ifdef WIN32
#define WAV16DATAWORD
#else
#define WAV16DATAWORD _huge
#endif
class MWAVE
{
private:
BOOL OpenFlage;
DWORDDataSize;
HGLOBALhData;
WAVDATA*lpData;
PCMWAVEFORMAT pFormat;
WAVEHDRWaveHead;
HWAVEOUT hWaveOut;
public:
MWAVE(){memset(this,0,sizeof(MWAVE));};
~MWAVE(){Close();};
int Open(char*);//打开一个WAV文件
int Play(HWND);//播放一个WAV文件
int Add(char*);//往正在播放的WAV设备中添加WAV 文件
int Stop();//停止播放
int Close();//关闭设备
};
#endif // !defined(AFX_WAVEMIX_H__54F4AF66_CE37_11D1_94F8_0000B431BBA1__INCLUDED_)
// wavemix.cpp:Defines the class behaviors for the application.
//
#include "stdafx.h"
#include "wavemix.h"
#include "wavemixDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
///////////////////////////////////////
// wavemix Class
//////////////////////////////////////
int MWAVE::Open(char* name)
{
HMMIO hMmio;
MMCKINFO pinfo;
MMCKINFO cinfo;
if(hMmio)Close();
//打开WAV文件,返回一个HMMIO句柄
hMmio=mmioOpen(name,NULL,MMIO_READ);
if(!hMmio)return FALSE;
OpenFlage=1;
//查找父块"wave";
pinfo.fccType=mmioFOURCC('W','A','V','E');
if(mmioDescend(hMmio,&pinfo,NULL,MMIO_FINDRIFF))goto FALSE_END;
//查找子块"fmt" parent"riff";
cinfo.ckid=mmioFOURCC('f','m','t',' ');
if(mmioDescend(hMmio,&cinfo,&pinfo,MMIO_FINDCHUNK))
goto FALSE_END;
mmioRead(hMmio,(LPSTR)&pFormat,sizeof(PCMWAVEFORMAT));//cinfo.cksize);
if(pFormat.wf.wFormatTag!=WAVE_FORMAT_PCM)
goto FALSE_END;
//跳入块"FMT"
mmioAscend(hMmio,&cinfo,0);
//查找数据块
cinfo.ckid=mmioFOURCC('d','a','t','a');
if(mmioDescend(hMmio,&cinfo,&pinfo,MMIO_FINDCHUNK))
goto FALSE_END;
DataSize=cinfo.cksize;
//读取数据
hData=GlobalAlloc(GMEM_MOVEABLE
| GMEM_SHARE,DataSize);
lpData=(WAVDATA*)GlobalLock(hData);
if( !hData || !lpData ) goto FALSE_END;
if(mmioRead(hMmio,(HPSTR)lpData,DataSize)
!=(LRESULT)DataSize)
goto FALSE_END;
//close and return
mmioClose(hMmio,MMIO_FHOPEN);
return TRUE;
FALSE_END:
if(hMmio)mmioClose(hMmio,MMIO_FHOPEN);
if(lpData)LocalUnlock(hData);
if(hData)GlobalFree(hData);
memset(this,0,sizeof(MWAVE));
return 0;
}
int MWAVE::Play(HWND hP)
{
if(!OpenFlage)return FALSE;
//检测系统播放功能
if(waveOutOpen(NULL,WAVE_MAPPER,
(WAVEFORMATEX*)&pFormat,NULL,
NULL,WAVE_FORMAT_QUERY))
return Close();
if(waveOutOpen(&hWaveOut,WAVE_MAPPER,
( WAVEFORMATEX*)&pFormat,(DWORD)hP,
0,CALLBACK_WINDOW))
return Close();
WaveHead.lpData=(LPSTR)lpData;
WaveHead.dwBufferLength=DataSize;
WaveHead.dwFlags=0L;
WaveHead.dwLoops=0L;
//往WAV设备中添加数据
if(waveOutPrepareHeader(hWaveOut,&WaveHead,
sizeof(WAVEHDR)))
return Close();
if(waveOutWrite(hWaveOut,&WaveHead,sizeof(WAVEHDR)))
return Close();
return TRUE;
}
//#define min(a, b) (((a) < (b)) ? (a) : (b))
int MWAVE::Add(char* name)
{
register int x;
if(!OpenFlage)return Open(name);
MWAVE wav;
if(!wav.Open(name))return FALSE;
MMTIME time;
//获得WAV文件当前播放位置
time.wType=TIME_BYTES;
if(waveOutGetPosition(hWaveOut,&time,sizeof(MMTIME)))
time.u.cb=0;
DWORD start=((time.u.cb>>1)<<1);
DWORD end=min(DataSize_start,wav.DataSize);
register WAVDATA* lpd=lpData+start;
for(register DWORD i=0;i<end;i++)
{
//将两组WAV文件数据相加,并检测数据大小是否合法,如果//数据大小越界,则分别取最大值和最小值
x=(((*(lpd+i))+(*(wav.lpData+i))))_128;
if(x<0)x=0;
if(x>255)x=255;
*(lpd+i)=(BYTE)(x);
}
return TRUE;
}
int MWAVE::Stop()
{return !waveOutReset(hWaveOut);}
int MWAVE::Close()
{
if(hWaveOut)
{
waveOutReset(hWaveOut);
waveOutClose(hWaveOut);
waveOutUnprepareHeader(hWaveOut,&WaveHead,
sizeof(WAVEHDR));
}
if(lpData)LocalUnlock(hData);
if(hData)GlobalFree(hData);
memset(this,0,sizeof(MWAVE));
return 0;
}