很久以前的一个作品,不过当时没在CSDN博客上发布。。。
本来想写个转成WMA的。。。不过关于WMA格式网上的资料实在太少,虽然也找到个“CD抓音轨转成WMA”的文章,但是这篇文章的代码里面引用了Windows Media SDK,我对这个玩意没多少研究,所以也只能望洋兴叹了。另外本文参考了C++实现CD抓轨转WAV,在此感谢作者的无私分享!好的,废话不多说,下面说下我制作这个软件的过程。
不知道各位读者对CD音频格式的了解是从什么时候开始的呢?呵呵。。。其实笔者从刚接触电脑不久的时候就开始接触CD光盘文件了,那时候Win98才刚刚出来,连VB也是在3、4年后才接触的,还记得那时候很困惑,为什么在我的电脑里面将光盘的TrackN.cda复制到硬盘上后再取出光盘就不能听音乐了呢?哈哈。。。后来在慢慢的摸索中才知道CDA文件只是一个文件头而已,只记录了CD音频数据的位置等信息而已,要获取真正的音频数据必须要用CD抓音轨软件将光盘上的音轨“抓”出来存到本地才行,还记得我当时使用的软件是当时非常流行的XingPlayer,自从知道了这个软件,我听音乐就不再需要换着光盘来听歌了,非常方便!时隔十年后的今天,我终于掌握了一些关于音频CD的有关知识,在此发出来与大家一起共享~~~
播放CD
在一些抓音轨的软件里面,一般都会提供CD音乐试听的功能,让用户知道哪首歌好听之后才有选择地保存该曲目。
另外需要说明的是,关于音频CD的操作方法有很多种,最常用的是用发送MCI指令,但本文为了让读者对音频CD的格式有个大致的了解,文中将不采用这种方法来讲述如何播放CD,如果有读者对MCI指令有兴趣可以上网自行搜索。本文将使用CreateFile、DeviceIoControl两个API来对音频CD进行操作,这两个函数的功能非常强大,一般可用于与系统底层驱动的通讯,但也可以发送一些IO控制命令至某些硬件设备来实现一些特殊功能。这两个函数的声明如下:
这两个函数的参数这里就不细说了,具体大家可以参考百度百科或MSDN的相应介绍。CreateFile可以获得一个设备的句柄,在本文中要获得光盘驱动器的句柄,可以将"//./X:"传入lpFileName参数来获取(其中X:为光盘驱动器的盘符);DeviceIoControl函数的dwIoControlCode参数可以传入要操作的IO指令,该函数若返回非零值表示操作成功,返回零表示操作失败。
注意:发送完IO指令后请不要忘记用CloseHandle函数将打开的设备句柄关闭。
我们要播放CD音乐,自然就需要有播放、暂停、停止这三个主要功能了,这三个功能对应的IO指令为:
这里对CDROM_PLAY_AUdio_MSF这个结构体的成员进行一下说明:
StartingM 播放开始时间的“分”部分(注:分英文为Minute);
StartingS 播放开始时间的“秒”部分(注:秒英文为Second);
StartingF 播放开始时间的“帧”部分(注:帧英文为Frame);
EndingM 播放结束时间的“分”部分;
EndingS 播放结束时间的“秒”部分;
EndingF 播放结束时间的“帧”部分。
注意:在音频CD中,音频数据是以一个一个扇区的方式来存储的,一个扇区存储一个帧的数据,而每75个帧的数据长度就是一秒。大家可能也会发现一些CD-R上面标有“74Min”的字样,其实这个字样意思是说如果按音频CD格式来记录CDA音频,总共可以存放74分钟的音乐。所以在这个结构体中,M成员的取值范围是0~74,S成员的取值范围是0~59,F成员的取值范围是0~74。当然现在也有用DVD做音频CD的,这样一来M成员的值就有可能不只74了,但是具体最大值可以为多少这个我没有详细了解,希望知道的读者朋友能不吝赐教。另外因为某些文章规范,音频CD的有效音频数据都是从第150帧开始记录的,所以要播放音频时应该在播放位置偏移值上加上150。
播放音频CD的代码很简单:
暂停/恢复/停止音频CD的播放那更就简单了,代码如下:
播放CD的难点是如何获取播放的开始/结束位置,这个将在下一节讲述。
获取音频CD相关信息
要抓取CD音轨,我们第一件事当然是要获取音频CD的有关信息了,比如曲目号、音轨偏移、音轨长度等,这些信息的获取都可以通过发送IO指令IOCTL_CDROM_READ_TOC来实现:
TrackNumber即为曲目号,(LastTrack-FirstTrack+1)即为音频CD里面的总曲目数量,Address()即为音轨的绝对偏移地址(注:绝对地址是指从整张音频CD起始地址开始计算的地址偏移值,相对地址是指从本曲目起始地址开始计算的地址偏移值),真实的扇区访问地址可以用下面的两个函数获得:
利用这两个函数便可以解决我们在第一节中如何获取某个曲目的播放起始/结束地址的问题了。
读取CD音频数据
“读取CD音频数据”就是我们通常说的“抓CD音轨”了,要实现这个功能可以使用IO指令IOCTL_CDROM_RAW_READ来实现:
DiskOffset 要读取的扇区地址的64位绝对偏移值;
SectorCount 要读取的扇区数目(此值将影响抓音轨时的速度,经我测试此值最大只能为55,大于此值读取音频CD将会有错误);
TrackMode 按何种类型来读取CD,这里要读取的是音频CD,所以此成员取值CDDA。
保存为WAVE波形音频文件
要将获取到的CD音频数据保存为WAV文件就需要了解WAVE波形音频文件的格式,这类文件的格式非常简单,详细的信息请参考这里:http://blog.csdn.net/superhero122/archive/2007/08/03/1724687.aspx
WAVEFORMATEX结构体
|
偏移地址 |
长度 |
数据类型 |
内容描述 |
&H00 |
4 |
String |
"RIFF"标识 |
|
&H04 |
4 |
Long |
||
&H08 |
4 |
String |
"WAVE"标识 |
|
&H0C |
4 |
String |
"fmt"标识(注意有空格的) |
|
&H10 |
4 |
Long |
WAVEFORMATEX结构体长度 |
|
&H14 |
2 |
Integer |
波形文件格式,一般为WAVE_FORMAT_PCM,脉冲编码调制格式 |
|
&H16 |
2 |
Integer |
通道数,单声道为1,立体声为2 |
|
&H18 |
2 |
Integer |
采样率(每秒样本数),表示每个通道的播放速度 |
|
&H1C |
4 |
Long |
波形音频数据传送速率,其值为通道数×每秒数据位数×每样本的数据位数/8。播放软件利用此值可以估计缓冲区的大小 |
|
&H20 |
2 |
Integer |
数据块的调整数(按字节算的),其值为通道数×每样本的数据位值/8。播放软件需要一次处理多个该值大小的字节数据,以便将其值用于缓冲区的调整 |
|
&H22 |
2 |
Integer |
每样本的数据位数,表示每个声道中各个样本的数据位数。如果有多个声道,对每个声道而言,样本大小都一样 |
|
&H24 |
4 |
String |
数据标识符"data" |
|
&H28 |
4 |
Long |
音频数据长度 |
|
&H2C |
? |
Byte() |
音频数据 |
了解了WAVE波形文件的格式后我们要保存波形文件便非常简单了,具体代码请参看文章最后提供的源码示例。
源码下载:http://hi.csdn.net/attachment/201103/23/0_13008860923N9B.gif