// conv.cpp // // convert a PCM wave to some other format //转换一个PCM格式的wav到其他格式 #include "stdafx.h" #include //包含头文件 windows.h #include //包含头文件mmsystem.h #include // Multimedia registration多媒体注册 #include // Audio Compression Manager音频压缩管理器 #include #include #include "conv.h" #include "Global.h" #pragma comment(lib,"MSAcm32.Lib") #pragma comment(lib,"winmm.lib") // Locate a driver that supports a given format and return its ID //寻找一个支持给定格式的驱动并返回其ID typedef struct { HACMDRIVERID hadid;//指向HACMDRIVERID的句柄 WORD wFormatTag;//32位无符号整型 格式标签 } FIND_DRIVER_INFO;//结构FIND_DRIVER_INFO包含2个元素:HACMDRIVERID hadid 和WORD wFormatTag // callback function for format enumeration //用来枚举格式的回调函数 BOOL CALLBACK find_format_enum(HACMDRIVERID hadid, LPACMFORMATDETAILS pafd, DWORD dwInstance, DWORD fdwSupport)//LPACMFORMATDETAILS pafd指向ACM格式详情的指针 { FIND_DRIVER_INFO* pdi = (FIND_DRIVER_INFO*) dwInstance;//FIND_DRIVER_INFO类型的指针 指向驱动信息 if (pafd->dwFormatTag == (DWORD)pdi->wFormatTag) //如果详情中的格式标签==驱动信息指针pdi中的格式标签 { // found it如果能找到 pdi->hadid = hadid; return FALSE; // stop enumerating停止枚举 } return TRUE; // continue enumerating继续枚举 } // callback function for driver enumeration //用来枚举驱动的回调函数 BOOL CALLBACK find_driver_enum(HACMDRIVERID hadid, DWORD dwInstance, DWORD fdwSupport) { FIND_DRIVER_INFO* pdi = (FIND_DRIVER_INFO*) dwInstance; // open the driver打开驱动 HACMDRIVER had = NULL;//HACMDRIVER类型的变量 had 指向ACM驱动的句柄 MMRESULT mmr = acmDriverOpen(&had, hadid, 0); if (mmr) { // some error错误 return FALSE; // stop enumerating停止枚举 } // enumerate the formats it supports枚举它支持的格式 DWORD dwSize = 0; mmr = acmMetrics((HACMOBJ)had, ACM_METRIC_MAX_SIZE_FORMAT, &dwSize); if (dwSize < sizeof(WAVEFORMATEX)) dwSize = sizeof(WAVEFORMATEX); // for MS-PCM WAVEFORMATEX* pwf = (WAVEFORMATEX*) malloc(dwSize); memset(pwf, 0, dwSize); pwf->cbSize = LOWORD(dwSize) - sizeof(WAVEFORMATEX); pwf->wFormatTag = pdi->wFormatTag; ACMFORMATDETAILS fd; memset(&fd, 0, sizeof(fd)); fd.cbStruct = sizeof(fd); fd.pwfx = pwf; fd.cbwfx = dwSize; fd.dwFormatTag = pdi->wFormatTag; mmr = acmFormatEnum(had, &fd, find_format_enum, (DWORD)(VOID*)pdi, 0); free(pwf); acmDriverClose(had, 0); if (pdi->hadid || mmr) { // found it or some error找到了 或 有错误 return FALSE; // stop enumerating停止枚举 } return TRUE; // continue enumeration继续枚举 } // locate the first driver that supports a given format tag //寻找支持给定格式标签的第一个驱动 HACMDRIVERID find_driver(WORD wFormatTag) { FIND_DRIVER_INFO fdi; fdi.hadid = NULL; fdi.wFormatTag = wFormatTag; MMRESULT mmr = acmDriverEnum(find_driver_enum, (DWORD)(VOID*)&fdi, 0); if (mmr) return NULL; return fdi.hadid; } // get a description of the first format supported for a given tag //获取驱动的描述 这个驱动是第一个支持给定标签的驱动。 WAVEFORMATEX* get_driver_format(HACMDRIVERID hadid, WORD wFormatTag) { // open the driver打开驱动 HACMDRIVER had = NULL; MMRESULT mmr = acmDriverOpen(&had, hadid, 0); if (mmr) { return NULL; } // allocate a structure for the info //为信息分配一个结构 DWORD dwSize = 0; mmr = acmMetrics((HACMOBJ)had, ACM_METRIC_MAX_SIZE_FORMAT, &dwSize); if (dwSize < sizeof(WAVEFORMATEX)) dwSize = sizeof(WAVEFORMATEX); // for MS-PCM WAVEFORMATEX* pwf = (WAVEFORMATEX*) malloc(dwSize); memset(pwf, 0, dwSize); pwf->cbSize = LOWORD(dwSize) - sizeof(WAVEFORMATEX); pwf->wFormatTag = wFormatTag; ACMFORMATDETAILS fd; memset(&fd, 0, sizeof(fd)); fd.cbStruct = sizeof(fd); fd.pwfx = pwf; fd.cbwfx = dwSize; fd.dwFormatTag = wFormatTag; // set up a struct to control the enumeration //建立一个结构用于控制枚举 FIND_DRIVER_INFO fdi; fdi.hadid = NULL; fdi.wFormatTag = wFormatTag; mmr = acmFormatEnum(had, &fd, find_format_enum, (DWORD)(VOID*)&fdi, 0); acmDriverClose(had, 0); if ((fdi.hadid == NULL) || mmr) { free(pwf); return NULL; } return pwf; } int PcmToALow(char *pFileName, char *pDescFileName) { FILE *fp,*fpCopy; MMRESULT mmr; HEADER *pPcmHEADER = new HEADER[sizeof(HEADER)]; FMT *pPcmFMT = new FMT[sizeof(FMT)]; DATA *pWaveHeaderData = new DATA[sizeof(DATA)]; int nLen = sizeof(HEADER) + sizeof(FMT) + sizeof(DATA) + 2; int nReadNum; int nFileSize; if((fp = fopen(pFileName, "rb")) == NULL) //读取文件 { return -1; } if((fpCopy = fopen(pDescFileName, "wb+")) == NULL) //为转换建立一个新文件 { return -1; } fseek(fp, 0, SEEK_SET); fseek(fp, 0, SEEK_END); nFileSize = ftell(fp); fseek(fp, 0, SEEK_SET); fread(pPcmHEADER,sizeof(unsigned short), sizeof(HEADER), fp ); DWORD nWaveFileSize = pPcmHEADER->dwSize; fseek(fp, sizeof(HEADER), SEEK_SET); fread( pPcmFMT, sizeof(unsigned short), sizeof(FMT), fp ); fseek(fp, sizeof(HEADER) + sizeof(FMT) + 2, SEEK_SET); //注意,+2是跳过保留的两位 fread(pWaveHeaderData,sizeof(unsigned short), sizeof(DATA), fp ); DWORD nPcmDataSize = pWaveHeaderData->dwSize; BYTE* pSrcData = new BYTE [nFileSize - nLen]; // 1 second duration memset(pSrcData, 0, nFileSize - nLen); fseek(fp, nLen, SEEK_SET); //nReadNum = fread(pSrcData, sizeof(unsigned short), pWaveHeaderData->dwSize, fp); nReadNum = fread(pSrcData, 1, nFileSize - nLen, fp); if( nReadNum == 0 ) //读文件错误 { return -1; } WAVEFORMATEX wfSrc; memset(&wfSrc, 0, sizeof(wfSrc)); wfSrc.cbSize = pPcmFMT->dwSize; wfSrc.wFormatTag = pPcmFMT->wFormatTag; // pcm wfSrc.nChannels = pPcmFMT->wChannels; // mono单声道 wfSrc.nSamplesPerSec = pPcmFMT->dwSamplesPerSec; // 8000 kHz wfSrc.wBitsPerSample = pPcmFMT->uiBitsPerSample; // 8 bit wfSrc.nBlockAlign = pPcmFMT->wBlockAlign; wfSrc.nAvgBytesPerSec = pPcmFMT->dwAvgBytesPerSec; // 选择一个要转换的格式 WORD wFormatTag = WAVE_FORMAT_ALAW; //现在我们选取一个支持目标格式的编码器(CODEC) HACMDRIVERID hadid = find_driver(wFormatTag); if (hadid == NULL) { exit(1); } //显示这个驱动的一些信息 ACMDRIVERDETAILS dd; dd.cbStruct = sizeof(dd); mmr = acmDriverDetails(hadid, &dd, 0); // 取得格式的详情 //注意:也许有多个目标格式支持给定的格式,而这只是第一个。 WAVEFORMATEX* pwfDrv = get_driver_format(hadid, wFormatTag); if (pwfDrv == NULL) { exit(1); } //取得一个驱动支持的PCM格式标签 //注意:我们只是选取了第一个支持PCM格式的,也许这个不是最好的选择。 WAVEFORMATEX* pwfPCM = get_driver_format(hadid, WAVE_FORMAT_PCM); if (pwfPCM == NULL) { exit(1); } //////////////////////////////////////////////////////////////////////////////// //转换源wave到编码器支持的PCM格式 //我们使用任何可以完成PCM到PCM转换的驱动 HACMSTREAM hstr = NULL; mmr = acmStreamOpen(&hstr, NULL, // 任意驱动 &wfSrc, // 源格式 pwfPCM, // 目标格式 NULL, // 无过滤 NULL, // 无回调 0, // 实例数据(未使用) ACM_STREAMOPENF_NONREALTIME); // flags if (mmr) { exit(1); } // 为转换结果开辟一个缓冲区 int nFileData = (nFileSize - nLen) * 2 - 6000; BYTE* pDst1Data = new BYTE [nFileData]; // 填写转换信息 memset(pDst1Data, 0, nFileData); // fill in the conversion info ACMSTREAMHEADER strhdr; memset(&strhdr, 0, sizeof(strhdr)); strhdr.cbStruct = sizeof(strhdr); strhdr.pbSrc = pSrcData; // the source data to convert strhdr.cbSrcLength = nFileSize - nLen; strhdr.pbDst = pDst1Data; strhdr.cbDstLength = nFileData; // prep the header mmr = acmStreamPrepareHeader(hstr, &strhdr, 0); // convert the data mmr = acmStreamConvert(hstr, &strhdr, 0); if (mmr) { exit(1); } // close the stream acmStreamClose(hstr, 0); /////////////////////////////////////////////////////////////////////////////////// // 将中间格式转换为最终的压缩格式 // 打开驱动程序 HACMDRIVER had = NULL; mmr = acmDriverOpen(&had, hadid, 0); if (mmr) { exit(1); } // 打开转换流 // 注意使用了ACM_STREAMOPENF_NONREALTIME标志. // 没有此标志一些软件压缩程序会报告512号错误--即不可能 mmr = acmStreamOpen(&hstr, had, // driver handle pwfPCM, // source format pwfDrv, // destination format NULL, // no filter NULL, // no callback 0, // instance data (not used) ACM_STREAMOPENF_NONREALTIME); // flags if (mmr) { exit(1); } // 为转换结果分配一个缓冲区 // 根据以字节计的平均速率计算输出缓冲区的尺寸 // 并加上一机动位(bit) // 没有此额外的空间IMA_ADPCM驱动程序将不能转换 DWORD dwDst2Bytes ;//= pwfDrv->nAvgBytesPerSec * dwDst1Bytes / pwfPCM->nSamplesPerSec; //dwDst2Bytes = (nFileSize - nLen) * 3 / 2; // 增加一点空间 dwDst2Bytes = nFileSize - nLen; // 增加一点空间 BYTE* pDst2Data = new BYTE [dwDst2Bytes]; #ifdef _DEBUG // 用0填充目标缓冲,由此我们可以在调试中看到转换了的东西 memset(pDst2Data, 0, dwDst2Bytes); #endif // 填充转换信息 ACMSTREAMHEADER strhdr2; memset(&strhdr2, 0, sizeof(strhdr2)); strhdr2.cbStruct = sizeof(strhdr2); strhdr2.pbSrc = pDst1Data; // the source data to convert strhdr2.cbSrcLength = nFileData; strhdr2.pbDst = pDst2Data; strhdr2.cbDstLength = dwDst2Bytes; // prep the header准备wav头 mmr = acmStreamPrepareHeader(hstr, &strhdr2, 0); // convert the data mmr = acmStreamConvert(hstr, &strhdr2, 0); if (mmr) { exit(1); } fclose(fp); // close the stream and driver关闭流和驱动 mmr = acmStreamClose(hstr, 0); mmr = acmDriverClose(had, 0); //生成转换完的ccitt格式文件 fseek(fpCopy, 0, SEEK_SET); strcpy(pPcmHEADER->fccID,"RIFF"); strcpy(pPcmHEADER->fccType,"WAVE"); pPcmHEADER->dwSize = nWaveFileSize; fseek(fpCopy,sizeof(HEADER),1); //跳过HEADER的长度,以便下面继续写入wav文件的数据; pPcmFMT->dwSamplesPerSec = 8000; pPcmFMT->dwAvgBytesPerSec = 8000;//wfSrc.nAvgBytesPerSec;//pPcmFMT->dwSamplesPerSec * sizeof(DATA); pPcmFMT->uiBitsPerSample = 8; strcpy(pPcmFMT->fccID, "fmt "); pPcmFMT->dwSize = wfSrc.cbSize; pPcmFMT->wBlockAlign = wfSrc.nBlockAlign; pPcmFMT->wChannels = 1; pPcmFMT->wFormatTag = WAVE_FORMAT_ALAW; //以上是创建wav头的FMT; fwrite(pPcmFMT,sizeof(FMT),1,fpCopy); //将FMT写入.wav文件; WORD nExBit = 0; fwrite(&nExBit,2, 1, fpCopy); //写入保留两位字节 //以下是创建wav头的DATA; 但由于DATA.dwsize未知所以不能写入.wav文件 strcpy(pWaveHeaderData->fccID,"data"); //以上是创建wav头的DATA; // fwrite(&pcmDATA,sizeof(DATA),1,fpCpy); pWaveHeaderData->dwSize = nPcmDataSize; //给pcmDATA.dwsize 0以便于下面给它赋值 //pWaveHeaderData->dwSize = nFileSize - nLen; //给pcmDATA.dwsize 0以便于下面给它赋值 fseek(fpCopy,sizeof(DATA),1); //跳过DATA的长度,以便以后再写入wav头的DATA; fwrite(pDst2Data, 1, dwDst2Bytes, fpCopy); pPcmHEADER->dwSize = 46 + nFileSize - nLen; //根据pcmDATA.dwsize得出pPcmHEADER->dwsize的值 rewind(fpCopy); //将fpCpy变为.wav的头,以便于写入HEADER和DATA; fwrite(pPcmHEADER,sizeof(HEADER),1,fpCopy); //写入HEADER fseek(fpCopy,sizeof(FMT) + 2,1); //跳过FMT,因为FMT已经写入 fwrite(pWaveHeaderData,sizeof(DATA),1,fpCopy); //写入DATA; fclose(fpCopy); //关闭文件 delete[] pDst1Data; delete[] pDst2Data; delete[] pSrcData; delete[] pWaveHeaderData; delete[] pPcmFMT; delete[] pPcmHEADER; return 0; }