wave文件头分很多种,参考:
https://blog.csdn.net/xsjm206/article/details/6727023
实际上更多。所以操作起来其实挺麻烦的。但是总的来说只要记住文件头大小即可了。44,58,60,90。
mmio函数操作步骤:
1。HMMIO hmmio = mmioOpen(strFileName, NULL, MMIO_ALLOCBUF | MMIO_READ);
PS:第二个参数需要注意:除非打开内存文件、为缓冲 I / O 指定缓冲区的大小或指定卸载的 I / O 过程以打开文件,否则此参数应为NULL。如果此参数不是NULL,则它引用的MMIOINFO 结构的所有未使用成员都必须设置为零,包括保留的成员。
2。操作文件。wave文件由各种chunk(块)构成,RIFF块,fmt块,fact块,data块。据说还有LIST块,不懂。
齐秦的《不必勉强》的wave文件头。
首先查找的一定是RIFF块。mmiodescend是用来查找子块的。
MMCKINFO ckinfo;
mmioDescend(hmmio, &ckinfo, NULL, 0);
PS:第三个参数是父块,因为是第一个块,所以写NULL。
第二个参数关键:
typedef struct _MMCKINFO
{
FOURCC ckid; /* chunk ID */
DWORD cksize; /* chunk size */
FOURCC fccType; /* form type or list type */
DWORD dwDataOffset; /* offset of data portion of chunk */
DWORD dwFlags; /* flags used by MMIO functions */
} MMCKINFO, *PMMCKINFO, NEAR *NPMMCKINFO, FAR *LPMMCKINFO;
这个结构体设计得很恶心。实际操作中,子块标识符ckid全是数字,让你调试时不知道查到哪里了。
RIFF:52-49-46-46(HEX),1179011410(十进制)。
fmt : 66-6D-74-20(HEX),544501094(十进制)
fact: 66-61-63-74(HEX),1952670054(十进制)
data:64-61-64-61(HEX),1635017060(十进制)
LIST:4C-49-53-54(HEX),1414744396(十进制)
PS:查十进制值时是倒序放的。01-02-03-04,字符存放都是正序的ABCD,计算数字则是倒着读的,04-03-02-01;
返回值为0时,表示查找成功。
查找子块时其实可以预先设定ckid,然后查找,这样就可以跳过中间块,直奔目标:
ckinfo.ckid = mmioFOURCC('d', 'a', 't', 'a') ;
MMCKINFO MIKF;
mmioDescend(hmmio, &MIKF, &ckinfo, MMIO_FINDCHUNK);
查找子块后退出mmioAscend(hmmio, &ckinfo, 0);
mmioDescend需要查找的数据:
1.文件大小:RIFF块(12字节)的cksize。不包括RIFF文件头ID和SIZE。即cksize+8。
2.WAVEFORMAT信息填充:fmt[空格]块,一般WAVE文件是44字节,其中的fmt块是24字节,也有26,40字节的。其中去掉开头的fmt标识符和cksize,即是16(18)字节的WAVEFORMAT。这部分内容需要另一个mmio函数,mmioRead
WAVEFORMAT pcmWaveFormat;
mmioRead(hmmio, (HPSTR)&pcmWaveFormat, sizeof(pcmWaveFormat));
3.数据区大小:data块的cksize。需要注意的是,这部分内容不包括8字节的块标识符。
4.数据区后面是音乐文件的标签,作者,唱片集,出版年代等信息。
3。操作音频数据:
MMIOINFO mminfo;
mmioGetInfo(hmmio, &mminfo, 0);
这个mmioinfo 太重要了。
typedef struct _MMIOINFO
{
/* general fields */
DWORD dwFlags; /* general status flags */
FOURCC fccIOProc; /* pointer to I/O procedure */
LPMMIOPROC pIOProc; /* pointer to I/O procedure */
UINT wErrorRet; /* place for error to be returned */
HTASK htask; /* alternate local task */
/* fields maintained by MMIO functions during buffered I/O */
LONG cchBuffer; /* size of I/O buffer (or 0L) */
HPSTR pchBuffer; /* start of I/O buffer (or NULL) */
HPSTR pchNext; /* pointer to next byte to read/write */
HPSTR pchEndRead; /* pointer to last valid byte to read */
HPSTR pchEndWrite; /* pointer to last byte to write */
LONG lBufOffset; /* disk offset of start of buffer */
/* fields maintained by I/O procedure */
LONG lDiskOffset; /* disk offset of next read or write */
DWORD adwInfo[3]; /* data specific to type of MMIOPROC */
/* other fields maintained by MMIO */
DWORD dwReserved1; /* reserved for MMIO use */
DWORD dwReserved2; /* reserved for MMIO use */
HMMIO hmmio; /* handle to open file */
} MMIOINFO, *PMMIOINFO, NEAR *NPMMIOINFO, FAR *LPMMIOINFO;
typedef const MMIOINFO FAR *LPCMMIOINFO;
其中mmioinfo.next即是读取数据的指针。但这个指针包括标识符,即data的ckid,cksize。由于我的文件50多兆,所以需要缓存输入,当选择数据总大小时,起初是选择了文件大小,最后播放文件到结尾时,程序出错。后来改为数据区大小,显示可以了。照理来说应该是datasize+8才对。
DWORD BufferSize=176400;
inf arraySize=BufferSize;
BYTE *pbWave;
while (dataSize > 0)
{
dataSize-=BufferSize;
if dataSize<BufferSize)
arraySize=dataSize;
pbWave = new BYTE[arraySize];
for(int b=0;b<arraySize;b++)
{
*((BYTE*)pbWave + b) = *((BYTE*)mminfo.pchNext);
}
//这数据已得到,输出到声卡就行了,我用的xaudio,这玩意是真TM不好用,有噪音不说,一旦出错,其他播放器也不能正常播放(我的耳机声卡可能太低档了,会出这问题)
delete pbWave[];
}
mmioClose(hmmio, 0);