微软使用此方法来加快多媒体文件的 I/O 操作.

如果不适用该类API转而直接使用系统函数fread等将降低效率

并且分配更多的内存: AppAppBufFileIOBufferMultimediaFile

使用 mmio 将省去AppBuf这一环节,直接读取操作系统的文件输入输出缓冲

#include <STDIO.H>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
-----------------
typedef struct tagMMIOINFO{
    DWORD dwFlags;     // 打开文件方式标志,取值见表12-11
    FOURCC fccIOProc;  // 标识文件IO过程的四字符代码,无IO过程时为NULL
    LPMMIOPROC pIOProc;// 文件IO过程的指针
    UINT wErrorRet;    // 扩展的错误值,只用于打开函数
    HTASK hTask;       // 局部IO过程的句柄
    LONG cchBuffer;    // 文件缓冲区的字节大小,对非缓冲文件为0
    HPSTR pchBuffer;   // 指向文件缓冲区的指针,对非缓冲文件为NULL
    HPSTR pchNext;     // 指向缓冲区的下一读/写位置的指针
    HPSTR pchEndRead;  // 指向缓冲区内的可读位置尾
    HPSTR pchEndWrite; // 指向缓冲区内的可写位置尾
    LONG lBufOffset;   // 保留
    LONG lDiskOffset;  // 当前文件位置(从文件头的偏移字节数)
    DWORD adwInfo[4];  // IO过程维护的状态信息
    DWORD dwReserved1; // 保留
    DWORD dwReserved2; // 保留
    HMMIO hmmio;       // 由打开函数返回的文件句柄
} MMIOINFO;

// dwFlags:
// MMIO_ALLOCBUF 打开缓冲文件
// MMIO_CREATE 创建新文件
// MMIO_READ 打开只读文件
// MMIO_WRITE 打开只写文件
// MMIO_READWRITE 打开读写文件
HMMIO mmioOpen( //return: 返回被打开文件的句柄,出错时返回 NULL;
    char* fileName,
    MMIOINFO*,  // 额外参数的指针定义见杂项,一般为 NULL
    DWORD dwFlags // 标识,见上
);
LONG mmioRead(  // return:返回实际写入的字节数
    HMMIO,  // 文件的句柄
    HPSTR pOutData,  // 读取数据后用来存放读取到数据的指针,一般是WAVEFORMATEX对象的地址再强制转换
    LONG  // 存放读取数据的缓冲区的大小
);
LONG mmioWrite(
    HMMIO,  // 文件的句柄
    HPSTR pSrcData,  // 待写入数据的指针,可以为char*
    LONG  // 待写入数据的字节数
);
MMRESULT mmioClose(HMMIO, UINT wFlags/*关闭标识,可取0*/);
MMRESULT mmioDescend( // 进入块,成功返回MMSYSERR_NOERROR(= 0)
    HMMIO hmmio,  // 文件句柄
    LPMMCKINFO lpck,  // 指向接收MMCKINFO结构的缓冲区指针
    LPMMCKINFO lpckParent,  // 指向指定父块之MMCKINFO结构的指针,无父块时取为NULL
    // MMIO_FINDLIST:列表块内的指定标识符块、MMIO_FINDRIFF:RIFF块内的指定标识符块
    // 搜索标志,可取值有:0:当前位置、MMIO_FINDCHUNK:指定块标识符
    UINT wFlags
);
MMRESULT mmioAscend( // 离开块,成功返回MMSYSERR_NOERROR(= 0)
    HMMIO hmmio,   // 文件句柄
    LPMMCKINFO lpck,  // 指向(已被mmioDescend填充的)MMCKINFO结构的指针
    UINT wFlags  // 保留,必须为0
);

// lpstrCommand大小写无关
MCIERROR WINAPI mciSendString(LPCSTR lpstrCommand, LPSTR lpstrReturnString, UINT uReturnLength, HWND hwndCallback);
// 执行效率高
MCIERROR WINAPI mciSendCommand(MCIDEVICEID mciId, UINT uMsg, DWORD dwParam1, DWORD dwParam2);

// mciSendString() 打开cd播放器(cdaudio)或者放音设备(waveaudio)
char buf[50];
MCIERROR mciError;
mciError = mciSendString("open cdaudio", buf, strlen(buf), NULL);
if (mciError) {mciGetErrorString((mciError, buf, strlen(buf)));}
// 也可以简单的这样:
mciSendString("open cdaudio", NULL, 0, NULL);
mciSendString("play cdaudio", NULL, 0, NULL);
mciSendString("pause cdaudio", NULL, 0, NULL);
mciSendString("resume cdaudio", NULL, 0, NULL);
mciSendString("stop cdaudio", NULL, 0, NULL);
mciSendString("close cdaudio", NULL, 0, NULL);
// 播放一个 wav,mid,avi 文件
mciSendString("open myfolder\\tada.wav alias aa", NULL, 0, NULL);
mciSendString("play aa wait", NULL, 0, NULL);
mciSendString("close aa", NULL, 0, NULL);
// 录制声音
mciSendString("open new type waveaudio alias aa", NULL, 0, NULL);
mciSendString("record aa", NULL, 0, NULL);
mciSendString("save aa 'c:\\program files\\aaa.wav' wait", NULL, 0, NULL);
mciSendString("close aa", NULL, 0, NULL);

// 打开 放音设备
MCI_OPEN_PARMS mciOpen;
UINT m_wDeviceID;
mciOpen.lpstrDeviceType = "avivideo";  // NULL:表示由MCI自动判断文件类型
mciOpen.lpstrElementName = "myfolder\\clock.avi";
mciSendCommand(0, MCI_OPEN, MCI_OPEN_ELEMENT, (DWORD)&mciOpen);
m_wDeviceID = mciOpen.wDeviceID;

// param
// MCI_WAIT :会等到 函数所指定的操作完成后才返回
// MCI_NOTIFY :设备完成一次操作后 post 一个 MM_MCINOTIFY
// MCI_OPEN_SHAREABLE :将设备或文件以共享的方式打开
// 播放
MCI_PLAY_PARMS mciPlay;
mciSendCommand(m_wDeviceID, MCI_PLAY, MCI_WAIT, (DWORD)&mciPlay);

// 暂停
MCI_PLAY_PARMS PlayParms;
mciSendCommand(m_wDeviceID, MCI_PAUSE, 0, (DWORD)(LPVOID)&PlayParms);

// 停止
mciSendCommand(m_wDeviceID, MCI_STOP, NULL, NULL);

// 跳跃, 跳转的目标时间单位为毫秒
MCI_SEEK_PARMS SeekParms;
SeekParms.dwTo = 4 * 1000;
mciSendCommand(m_wDeviceID, MCI_SEEK, MCI_TO | MCI_WAIT, (DWORD)(LPVOID)&SeekParms);

// 跳到文件头
mciSendCommand(m_wDeviceID, MCI_SEEK, MCI_SEEK_TO_START, NULL);

// 跳到文件尾
mciSendCommand(m_wDeviceID, MCI_SEEK, MCI_SEEK_TO_END, NULL);

// 录音
MCI_RECORD_PARMS RecordParms;
mciSendCommand(m_wDeviceID, MCI_RECORD, NULL, (DWORD)(LPVOID)&RecordParms);

// 保存录音
MCI_SAVE_PARMS saveParms;
saveParms.lpfilename = "c:\\wavefile.wav";
mciSendCommand(m_wDeviceID, MCI_SAVE, MCI_SAVE_FILE | MCI_WAIT, (DWORD)(LPVOID)&saveParms);