程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> C++實現CD抓軌轉WAV

C++實現CD抓軌轉WAV

編輯:關於VC++

現在介紹一下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參數

得到了設備句柄,我們就可以用DeviceIoControl來獲息相關信息.

DeviceIoControl函數原型:

BOOL DeviceIoControl(
  HANDLE hDevice,  // 設備句柄
  DWORD dwIoControlCode,  // 控制碼
  LPVOID lpInBuffer,  // 輸入數據緩沖區指針
  DWORD nInBufferSize,  // 輸入數據緩沖區長度
  LPVOID lpOutBuffer,  // 輸出數據緩沖區指針
  DWORD nOutBufferSize,  // 輸出數據緩沖區長度
  LPDWORD lpBytesReturned,  // 輸出數據實際長度單元長度
  LPOVERLAPPED lpOverlapped           // 重疊操作結構指針
  );

4、獲取曲目

使用IOCTL_CDROM_READ_TOC控制碼輸出CDROM_TOC結構

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、讀取曲目信息

使用IOCTL_CDROM_RAW_READ輸入RAW_READ_INFO結構信息,輸出來獲取區域內容

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、文件生成

WAVE文件是非常簡單的一種RIFF文件,它的格式類型為"WAVE"。RIFF塊包 含兩個子塊,這兩個子塊的ID分別是"fmt"和"data",其中 "fmt"子塊由結構WAVEFORMATEX所組成,其子塊的大小就是sizeofof (WAVEFORMATEX),數據組成就是WAVEFORMATEX結構中的數據。WAVE文件的結構如下圖所示 :

標志符(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.nB lockAlign;

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文件頭

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、寫入音頻數據

把音頻數據放到WAV文件頭後寫入

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 測試通過。

下載源代碼:http://www.vckbase.net/code/viewcode.asp?id=2278

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved