新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     >>计算机科学论坛<<     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → C++实现CD抓轨转WAV 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 3477 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: C++实现CD抓轨转WAV 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 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参数
    得到了设备句柄,我们就可以用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.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文件头
    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();


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/12/17 14:01:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/5/13 22:42:30

    本主题贴数1,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    62.500ms