分类:
2008-10-13 16:48:03
C++实现CD抓轨转WAV
作者:
现在介绍一下C++实现CD抓轨转WAV,CD抓轨的方法有好几种,现在介绍其中一种。我们可以通过API函数CreateFile获得设备句柄,再用API函数DeviceIoControl来实现对设备的访问获取信息。再此还会用到WAVE文件结构WAVEFORMATEX,再把读到的信息写到文件里生成WAVE格式的文件。
我们要用到的头文件有: ntddcdrm.h(NTDDK开发包) winioctl.h
Mmreg.h
1、搜索光驱
我们可以用GetDriveType来判断设备类型,5为CDROM类型。返回类型可以参看MSDN,里面有详细介绍。
2、打开设备
用CreateFile获得设备句柄,例子如下:
HANDLE m_hDevice; CString FileName=”F:”; m_hDevice =CreateFile("\\\\.\\"+FileName, // 文件名路径 GENERIC_READ, // 读写方式 FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式 NULL, // 默认的安全描述符 OPEN_EXISTING, // 创建方式 0, // 不需设置文件属性 NULL); // 不需参照模板文件3、读取CD参数
BOOL DeviceIoControl( HANDLE hDevice, // 设备句柄 DWORD dwIoControlCode, // 控制码 LPVOID lpInBuffer, // 输入数据缓冲区指针 DWORD nInBufferSize, // 输入数据缓冲区长度 LPVOID lpOutBuffer, // 输出数据缓冲区指针 DWORD nOutBufferSize, // 输出数据缓冲区长度 LPDWORD lpBytesReturned, // 输出数据实际长度单元长度 LPOVERLAPPED lpOverlapped // 重叠操作结构指针 );
BOOL bResult; DWORD dwOutBytes; CDROM_TOC CdromTOC; //曲目信息结构,详细请看MSDN bResult=DeviceIoControl(m_hDevice, IOCTL_CDROM_READ_TOC,NULL,0, &CdromTOC, sizeof(CdromTOC), &dwOutBytes, (LPOVERLAPPED)NULL);5、获取曲目始点
DWORD CCdToWavDlg::GetStartSector(int track) { return (CdromTOC.TrackData[track-1].Address[1]*60*75 + CdromTOC.TrackData[track-1].Address[2]*75 + CdromTOC.TrackData[track-1].Address[3])-150; }6、获取曲目终点
DWORD CCdToWavDlg::GetEndSector(int track) { return (CdromTOC.TrackData[track].Address[1]*60*75 + CdromTOC.TrackData[track].Address[2]*75 + CdromTOC.TrackData[track].Address[3])-151; }7、读取曲目信息
BOOL CCdToWavDlg::ReadSector(int sector,BYTE Buffer[], int NumSectors) { DWORD dwOutBytes; RAW_READ_INFO rri; //结构详细请看MSDN rri.TrackMode =(TRACK_MODE_TYPE)2; rri.SectorCount = (DWORD)NumSectors; rri.DiskOffset =(DWORD64)(sector*CB_CDROMSECTOR); if (DeviceIoControl(m_hDevice,IOCTL_CDROM_RAW_READ, &rri, sizeof(rri), Buffer, (DWORD)NumSectors*CB_AUDIO,&dwOutBytes, (LPOVERLAPPED)NULL)) return true; return false; }8、 文件生成
标志符(RIFF) |
数据大小 |
格式类型("WAVE") |
"fmt" |
Sizeof(WAVEFORMATEX) |
WAVEFORMATEX |
"data" |
声音数据大小 |
声音数据 |
WAVEFORMATEX结构原型:
typedef struct { WORD wFormatTag; //编码格式,包括WAVE_FORMAT_PCM,WAVEFORMAT_ADPCM等 WORD nChannels; //声道数,单声道为1,双声道为2 DWORD nSamplesPerSec; //采样频率 DWORD nAvgBytesPerSec; //每秒的数据量 WORD nBlockAlign; //块对齐 WORD wBitsPerSample; //WAVE文件的采样大小 WORD cbSize; } WAVEFORMATEX; *PWAVEFORMATEX;9、定义WAVE文件结构
DWORD m_WaveHeaderSize = 38; DWORD m_WaveFormatSize = 18; DWORD m_AudioDataSize =0; DWORD m_WrittenBytes = 0; WAVEFORMATEX m_WaveFormatEx; m_WaveFormatEx.wFormatTag=WAVE_FORMAT_PCM ; m_WaveFormatEx.nSamplesPerSec=48000; m_WaveFormatEx.wBitsPerSample=16; m_WaveFormatEx.nChannels=2; m_WaveFormatEx.cbSize=0; m_WaveFormatEx.nBlockAlign=m_WaveFormatEx.nChannels*(m_WaveFormatEx.wBitsPerSample/8); m_WaveFormatEx.nAvgBytesPerSec=m_WaveFormatEx.nSamplesPerSec*m_WaveFormatEx.nBlockAlign;10、创建新文件
CFile m_file; CFileException fileException; CString m_csFileName= m_SavePath; m_file.Open(m_csFileName,CFile::modeCreate|CFile::modeReadWrite, &fileException); int StartSect=GetStartSector(m_List.GetCurSel()+1); int EndSect=GetEndSector(m_List.GetCurSel()+1); DWORD Bytes2Read=(EndSect - StartSect)*CB_AUDIO; m_AudioDataSize=Bytes2Read; BYTE Data[CB_AUDIO*NSECTORS];11、写入WAV文件头
m_file.SeekToBegin(); m_file.Write("RIFF",4); unsigned int Sec=(m_AudioDataSize + m_WaveHeaderSize); m_file.Write(&Sec,sizeof(Sec)); m_file.Write("WAVE",4); m_file.Write("fmt ",4); m_file.Write(&m_WaveFormatSize,sizeof(m_WaveFormatSize)); m_file.Write(&m_WaveFormatEx.wFormatTag,sizeof(m_WaveFormatEx.wFormatTag)); m_file.Write(&m_WaveFormatEx.nChannels,sizeof(m_WaveFormatEx.nChannels)); m_file.Write(&m_WaveFormatEx.nSamplesPerSec,sizeof(m_WaveFormatEx.nSamplesPerSec)); m_file.Write(&m_WaveFormatEx.nAvgBytesPerSec,sizeof(m_WaveFormatEx.nAvgBytesPerSec)); m_file.Write(&m_WaveFormatEx.nBlockAlign,sizeof(m_WaveFormatEx.nBlockAlign)); m_file.Write(&m_WaveFormatEx.wBitsPerSample,sizeof(m_WaveFormatEx.wBitsPerSample)); m_file.Write(&m_WaveFormatEx.cbSize,sizeof(m_WaveFormatEx.cbSize)); m_file.Write("data",4); m_file.Write(&m_AudioDataSize,sizeof(m_AudioDataSize));12、写入音频数据
DWORD m_seek=46; //文件头长度为46个字,必须从46后写入 for (int sector = StartSect; (sector < EndSect); sector+=NSECTORS) { int Sectors2Read = ( (sector + NSECTORS) < EndSect )?NSECTORS:(EndSect-sector); if (ReadSector(sector, Data, Sectors2Read)) { m_file.Write(Data,CB_AUDIO*Sectors2Read); m_file.Seek(m_seek+=CB_AUDIO*Sectors2Read,CFile::begin); } } m_file.Close();详细请看源代码。以上在 VC7+Window2000+NTDDK 测试通过。