如何在 C# 中验证音频 CD 上的 cda 文件是 wav 文件的结果
How to verify that a cda file on Audio CD is the result of a wav file in C#
我已经编写了一个代码段来将 wav 文件刻录到音频 CD 中。
它工作正常,但是,在执行验证的最后一步,它在某些 DVD 驱动器上失败。这些文件实际上已刻录到 CD 中,可以毫无问题地播放。但是验证似乎无缘无故地失败了。我可以关闭验证。但是,我更喜欢编写另一个函数来手动检查烧录的文件并验证它们是否是 wav 文件的实际结果。我能够做到这一点来刻录数据 CD。但是对于音频CD,因为它将它们转换成光盘上的cda文件,所以我无法比较它们。关于如何使用 C# 验证它们的任何建议?基本上让我们假设我有一张音频 CD,里面有几个 .cda 文件,我想确保它们是原始 wav 文件的实际转换文件。我知道 cda 文件只是占位符,我只是不知道如何从中获取 wav 文件(如果可能的话)以与原始 wav 文件进行比较。
正在将 cda 文件转换为 wav
将 cda 文件转换为 wav 文件并不是那么容易。
您必须使用一些非托管内存和指针才能从 cd 读取数据。
读CD方法:
readcd 程序验证驱动器是否为 cdrom 驱动器
然后通过在
中调用 CreateFile 获取驱动器句柄
Kernel32.
接下来我们使用该句柄查看驱动器是否已准备好使用
读取
kerenl32 中的 DeviceIoControl。
如果驱动器准备就绪,那么我们会查看它是否具有有效的 Table 内容
(以下简称 TOC)再次使用 DeviceIoControl.
如果 TOC 有效,那么接下来我们使用 DeviceIoControl 读取 TOC。
使用 TOC 我们可以确定 CD 上有多少曲目(没有
在这里提交 IO;我们已经有了目录)。然后在迭代中
我们进行的所有曲目。
我们创建一个二进制写入器用于写入二进制文件。
我们将 TOC 轨道数据转换为一个名为
的 kernel32 结构
TRACK_DATA.
使用该结构,我们能够确定哪个部门拥有
该曲目的开头。
而第l扇区会比第l扇区的起始扇区少一个扇区
下一首曲目。旁注:有很多指向结构和字节的指针
数组所以在
之间也有很多来回转换
他们。
磁道大小以扇区数减去开始
表示
从头开始。
现在我们遍历该轨道中的所有扇区。
我们创建了一个kernel32RAW_READ_INFO结构,用于
读取扇区的 DeviceIoControl 调用。
该结构通知 DeviceIoControl 调用我们正在读取一个
CD 并且我们正在读取一个扇区,该扇区位于光盘上。
(记住 CD 扇区与 HD 扇区略有不同;更多关于
后者。)
现在我们通过 DeviceIoControl 读取该扇区。如果成功
然后我们检索刚刚读取的扇区数据。
将扇区数据放入TrackData中合适的位置
锯齿状数组。
重复轨道中的所有扇区。
重复 CD 上的所有曲目。
使用 kerenl32 中的 CloseHandle 关闭驱动器的句柄。
// this functions reads binary audio data from a cd and stores it in a jagged array called TrackData
// it uses only low level file io calls to open and read the Table of Content and then the binary 'music' data sector by sector
// as discovered from the table of content
// it also writes it to a binary file called tracks with not extension
// this file can be read by any decent hex editor
void readcd()
{
bool TocValid = false;
IntPtr cdHandle = IntPtr.Zero;
CDROM_TOC Toc = null;
int track, StartSector, EndSector;
BinaryWriter bw;
bool CDReady;
uint uiTrackCount, uiTrackSize, uiDataSize;
int i;
uint BytesRead, Dummy;
char Drive = (char)cmbDrives.Text[0];
TRACK_DATA td;
int sector;
byte[] SectorData;
IntPtr pnt;
Int64 Offset;
btnStart.Enabled = false;
Dummy = 0;
BytesRead = 0;
CDReady = false;
Toc = new CDROM_TOC();
IntPtr ip = Marshal.AllocHGlobal((IntPtr)(Marshal.SizeOf(Toc)));
Marshal.StructureToPtr(Toc, ip, false);
// is it a cdrom drive
DriveTypes dt = GetDriveType(Drive + ":\");
if (dt == DriveTypes.DRIVE_CDROM)
{
// get a Handle to control the drive with
cdHandle = CreateFile("\\.\" + Drive + ':', GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
CDReady = DeviceIoControl(cdHandle, IOCTL_STORAGE_CHECK_VERIFY, IntPtr.Zero, 0, IntPtr.Zero, 0, ref Dummy, IntPtr.Zero) == 1;
if (!CDReady)
{
MessageBox.Show("Drive Not Ready", "Drive Not Ready", MessageBoxButtons.OK);
}
else
{
uiTrackCount = 0;
// is the Table of Content valid?
TocValid = DeviceIoControl(cdHandle, IOCTL_CDROM_READ_TOC, IntPtr.Zero, 0, ip, (uint)Marshal.SizeOf(Toc), ref BytesRead, IntPtr.Zero) != 0;
//fetch the data from the unmanaged pointer back to the managed structure
Marshal.PtrToStructure(ip, Toc);
if (!TocValid)
{
MessageBox.Show("Invalid Table of Content ", "Invalid Table of Content ", MessageBoxButtons.OK);
}
else
{
// really only nescary if there are un-useable tracks
uiTrackCount = Toc.LastTrack;
//for (i = Toc.FirstTrack - 1; i < Toc.LastTrack; i++)
//{
// if (Toc.TrackData[i].Control == 0)
// uiTrackCount++;
//}
// create a jagged array to store the track data
TrackData = new byte[uiTrackCount][];
// read all the tracks
for (track = 1; track <= uiTrackCount; track++)//uiTrackCount; track++)
{
Offset = 0;// used to store Sectordata into trackdata
label1.Text = "Reading Track" + track.ToString() + " of " + uiTrackCount.ToString(); ;
Application.DoEvents();
// create a binary writer to write the track data
bw = new BinaryWriter(File.Open(Application.StartupPath + "\Track" + track.ToString (), FileMode.Create));
//The CDROM_TOC-structure contains the FirstTrack (1) and the LastTrack (max. track nr). CDROM_TOC::TrackData[0] contains info of the
//first track on the CD. Each track has an address. It represents the track's play-time using individual members for the hour, minute,
//second and frame. The "frame"-value (Address[3]) is given in 1/75-parts of a second -> Remember: 75 frames form one second and one
//frame occupies one sector.
//Find the first and last sector of the track
td = Toc.TrackData[track - 1];
// minutes Seconds fractional seconds 150 bytes is the 2 second lead in to track 1
StartSector = (td.Address_1 * 60 * 75 + td.Address_2 * 75 + td.Address_3) - 150;
td = Toc.TrackData[track];
EndSector = (td.Address_1 * 60 * 75 + td.Address_2 * 75 + td.Address_3) - 151;
progressBar1.Minimum = StartSector;
progressBar1.Maximum = EndSector;
uiTrackSize = (uint)(EndSector - StartSector) * CB_AUDIO;//CB_AUDIO==2352
// how big is the track
uiDataSize = (uint)uiTrackSize;
//Allocate for the track
TrackData[track - 1] = new byte[uiDataSize];
SectorData = new byte[CB_AUDIO * NSECTORS];
// read all the sectors for this track
for (sector = StartSector; (sector < EndSector); sector += NSECTORS)
{
Debug.Print(sector.ToString("X2"));
RAW_READ_INFO rri = new RAW_READ_INFO();// contains info about the sector to be read
rri.TrackMode = TRACK_MODE_TYPE.CDDA;
rri.SectorCount = (uint)1;
rri.DiskOffset = sector * CB_CDROMSECTOR;
//get a pointer to the structure
Marshal.StructureToPtr(rri, ip, false);
// allocate an unmanged pointer to hold the data read from the disc
int size = Marshal.SizeOf(SectorData[0]) * SectorData.Length;
pnt = Marshal.AllocHGlobal(size);
//Sector data is a byte array to hold data from each sector data
// initiallize it to all zeros
SectorData.Initialize();
// read the sector
i = DeviceIoControl(cdHandle, IOCTL_CDROM_RAW_READ, ip, (uint)Marshal.SizeOf(rri), pnt, (uint)NSECTORS * CB_AUDIO, ref BytesRead, IntPtr.Zero);
if (i == 0)
{
MessageBox.Show("Bad Sector Read", "Bad Sector Read from sector " + sector.ToString("X2"), MessageBoxButtons.OK);
break;
}
progressBar1.Value = sector; // return the pointers to their respective managed data sources
Marshal.PtrToStructure(ip, rri);
Marshal.Copy(pnt, SectorData, 0, SectorData.Length);
Marshal.FreeHGlobal(pnt);
Array.Copy(SectorData, 0, TrackData[track - 1], Offset, BytesRead);
Offset += BytesRead;
}
// write the binary data nad then close it
bw.Write(TrackData[track - 1]);
bw.Close();
}
//unlock
PREVENT_MEDIA_REMOVAL pmr = new PREVENT_MEDIA_REMOVAL();
pmr.PreventMediaRemoval = 0;
ip = Marshal.AllocHGlobal((IntPtr)(Marshal.SizeOf(pmr)));
Marshal.StructureToPtr(pmr, ip, false);
DeviceIoControl(cdHandle, IOCTL_STORAGE_MEDIA_REMOVAL, ip, (uint)Marshal.SizeOf(pmr), IntPtr.Zero, 0, ref Dummy, IntPtr.Zero);
Marshal.PtrToStructure(ip, pmr);
Marshal.FreeHGlobal(ip);
}
}
}
//Close the CD Handle
CloseHandle(cdHandle);
ConvertToWav();
}
ConvertToWav 方法:
- 初始化 .wav 所需的四个 ChunkIds header。
然后我们将三个主要chunk的各个部分初始化为
代表PCM、Stereo、44100 Samples per second等方面
代表真正的 CD 数据。
接下来,我们遍历锯齿状
中表示的所有轨道
数组 TrackData.
创建一个名为 "Track(x).wav" 的文件,并使用
return 它的句柄
在 Kernel32.CreateFile.
构建 Header.
- 添加 "Music" 数据。
使用 Kernel32 中的 WriteFile 写入文件。
如果成功则继续。
我们刷新 WriteFile 中使用的所有缓冲区。
然后我们使用 CloseHandle 关闭文件。
Return 并进行下一曲,直到全部完成。
现在我们去播放 wav 文件并幸灾乐祸我们有多熟
// this procedure tacks the biary data stored in the jagged array called TraackData
// and, using low level file io functions) writes it out as a .wav file called trackx.wav
private void ConvertToWav()
{
int i, j, k, track, tracks;
byte[] b;
char[] riffchunk ={ 'R', 'I', 'F', 'F' };
char[] wavechunk ={ 'W', 'A', 'V', 'E' };
char[] datachunk ={ 'd', 'a', 't', 'a' };
char[] fmtchunk ={ 'f', 'm', 't', ' ' };
Int32 riffsize, datasize, fmtsize, extrabits;
Int32 DI, SampleRate, ByteRate;
uint BytesWritten;
Int16 BlockAlign, Format, NumChannels, BitsPerSample;
Byte[] Image;
IntPtr FileHandle;
Format = 1; // PCM
NumChannels = 2;// Stereo
SampleRate = 44100;// 44100 Samples per secon
BitsPerSample = 16; // 16 bits per sample
ByteRate = SampleRate * NumChannels * BitsPerSample / 8;
BlockAlign = 4;
fmtsize = 0x12;// size of the 'fmt ' chunk is 18 bytes
// get the number of tarcks stoerd in track data
tracks = TrackData.GetUpperBound(0);
// setup the progressbar
progressBar1.Maximum = tracks;
progressBar1.Minimum = 0;
// do all the tracks
for (track = 0; track <= tracks; track++)
{
DI = 0;//IDI is an index into the Image array where the next chunk of data will be stored
progressBar1.Value = track;
label1.Text = "Writeing Track " + (track + 1).ToString() + ".wav";
Application.DoEvents();
// Create a File called trackx.wav and return a handle to it
FileHandle=CreateFile(Application.StartupPath + "\Track" + (track + 1).ToString() + ".wav",GENERIC_WRITE,0,IntPtr.Zero ,OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero );
// Wav file format is notthe subject of this project .. .
// suffice it to say that at minimum there is a Header which is followed by the PCM, Stereo , 44100 Hz Sample rate binary data
// for more info on Wav format plese visit:
//http://www.sonicspot.com/guide/wavefiles.html
//Start prepareing the RIFF header
// how big the the 'music' binary data
datasize = TrackData[track].Length;
//build the header
riffsize = datasize;
riffsize += 4;//RIFFSize
riffsize += 4;//WAVE
riffsize += 4;//fmt
riffsize += fmtsize;
riffsize += 4;// DATA
riffsize += 4;//datasize
extrabits = 0;
// build the image
Image = new Byte[riffsize + 8];// riffchunk + riffsize
b = Encoding.ASCII.GetBytes(riffchunk);
Array.Copy(b, 0, Image, DI, 4);
DI += 4;
b = BitConverter.GetBytes(riffsize);
if (!BitConverter.IsLittleEndian)
Array.Reverse(b);
Array.Copy(b, 0, Image, DI, 4);
DI += 4;
b = Encoding.ASCII.GetBytes(wavechunk);
Array.Copy(b, 0, Image, DI, 4);
DI += 4;
b = Encoding.ASCII.GetBytes(fmtchunk);
if (!BitConverter.IsLittleEndian)
Array.Reverse(b);
Array.Copy(b, 0, Image, DI, 4);
DI += 4;
b = BitConverter.GetBytes(fmtsize);
if (!BitConverter.IsLittleEndian)
Array.Reverse(b);
Array.Copy(b, 0, Image, DI, 4);
DI += 4;
b = BitConverter.GetBytes(Format);
if (!BitConverter.IsLittleEndian)
Array.Reverse(b);
Array.Copy(b, 0, Image, DI, 2);
DI += 2;
b = BitConverter.GetBytes(NumChannels);
if (!BitConverter.IsLittleEndian)
Array.Reverse(b);
Array.Copy(b, 0, Image, DI, 2);
DI += 2;
b = BitConverter.GetBytes(SampleRate);
if (!BitConverter.IsLittleEndian)
Array.Reverse(b);
Array.Copy(b, 0, Image, DI, 4);
DI += 4;
b = BitConverter.GetBytes(ByteRate);
if (!BitConverter.IsLittleEndian)
Array.Reverse(b);
Array.Copy(b, 0, Image, DI, 4);
DI += 4;
b = BitConverter.GetBytes(BlockAlign);
if (!BitConverter.IsLittleEndian)
Array.Reverse(b);
Array.Copy(b, 0, Image, DI, 2);
DI += 2;
b = BitConverter.GetBytes(BitsPerSample);
if (!BitConverter.IsLittleEndian)
Array.Reverse(b);
Array.Copy(b, 0, Image, DI, 2);
DI += 2;
b = BitConverter.GetBytes(extrabits);
if (!BitConverter.IsLittleEndian)
Array.Reverse(b);
Array.Copy(b, 0, Image, DI, 2);
DI += 2;
b = Encoding.ASCII.GetBytes(datachunk);
Array.Copy(b, 0, Image, DI, 4);
DI += 4;
b = BitConverter.GetBytes(datasize);
if (!BitConverter.IsLittleEndian)
Array.Reverse(b);
Array.Copy(b, 0, Image, DI, 4);
DI += 4;
// add the digital 'music' data retrieved earler
Array.Copy(TrackData[track], 0, Image, DI, TrackData[track].Length);
// write the binary file - trackx.wav
i = WriteFile(FileHandle, Image, (uint)Image.Length, out BytesWritten, IntPtr.Zero);
//if successful then
// flush all buffers used in the low level write operation
// then close the file
if(i!= 0)
{
//Flush the file buffers to force writing of the data.
i = FlushFileBuffers(FileHandle);
//Close the file.
i = CloseHandle(FileHandle);
}
// the wave file now exists (created by reading the CD and can be playedby most wav players
Image = null;
progressBar1.Value = track;
}
}
文件比较
此方法在检查所有字节是否相等之前进行验证。
- 当两个文件的长度不同时,它们不可能是同一个文件,false 将被 returned
之后,该方法比较所有字节,return如果所有字节都等于
,则成功
private bool FileCompare(string file1, string file2)
{
int file1byte;
int file2byte;
FileStream fs1;
FileStream fs2;
// Open the two files.
fs1 = new FileStream(file1, FileMode.Open);
fs2 = new FileStream(file2, FileMode.Open);
// Check the file sizes. If they are not the same, the files
// are not the same.
if (fs1.Length != fs2.Length)
{
// Close the file
fs1.Close();
fs2.Close();
// Return false to indicate files are different
return false;
}
// Read and compare a byte from each file until either a
// non-matching set of bytes is found or until the end of
// file1 is reached.
do
{
// Read one byte from each file.
file1byte = fs1.ReadByte();
file2byte = fs2.ReadByte();
}
while ((file1byte == file2byte) && (file1byte != -1));
// Close the files.
fs1.Close();
fs2.Close();
// Return the success of the comparison. "file1byte" is
// equal to "file2byte" at this point only if the files are
// the same.
return ((file1byte - file2byte) == 0);
}
链接:
我已经编写了一个代码段来将 wav 文件刻录到音频 CD 中。 它工作正常,但是,在执行验证的最后一步,它在某些 DVD 驱动器上失败。这些文件实际上已刻录到 CD 中,可以毫无问题地播放。但是验证似乎无缘无故地失败了。我可以关闭验证。但是,我更喜欢编写另一个函数来手动检查烧录的文件并验证它们是否是 wav 文件的实际结果。我能够做到这一点来刻录数据 CD。但是对于音频CD,因为它将它们转换成光盘上的cda文件,所以我无法比较它们。关于如何使用 C# 验证它们的任何建议?基本上让我们假设我有一张音频 CD,里面有几个 .cda 文件,我想确保它们是原始 wav 文件的实际转换文件。我知道 cda 文件只是占位符,我只是不知道如何从中获取 wav 文件(如果可能的话)以与原始 wav 文件进行比较。
正在将 cda 文件转换为 wav
将 cda 文件转换为 wav 文件并不是那么容易。
您必须使用一些非托管内存和指针才能从 cd 读取数据。
读CD方法:
readcd 程序验证驱动器是否为 cdrom 驱动器
然后通过在
中调用 CreateFile 获取驱动器句柄 Kernel32.接下来我们使用该句柄查看驱动器是否已准备好使用
读取 kerenl32 中的 DeviceIoControl。如果驱动器准备就绪,那么我们会查看它是否具有有效的 Table 内容 (以下简称 TOC)再次使用 DeviceIoControl.
如果 TOC 有效,那么接下来我们使用 DeviceIoControl 读取 TOC。
使用 TOC 我们可以确定 CD 上有多少曲目(没有
在这里提交 IO;我们已经有了目录)。然后在迭代中
我们进行的所有曲目。我们创建一个二进制写入器用于写入二进制文件。
我们将 TOC 轨道数据转换为一个名为
的 kernel32 结构 TRACK_DATA.使用该结构,我们能够确定哪个部门拥有 该曲目的开头。
而第l扇区会比第l扇区的起始扇区少一个扇区 下一首曲目。旁注:有很多指向结构和字节的指针 数组所以在
之间也有很多来回转换 他们。磁道大小以扇区数减去开始
表示 从头开始。现在我们遍历该轨道中的所有扇区。
我们创建了一个kernel32RAW_READ_INFO结构,用于
读取扇区的 DeviceIoControl 调用。该结构通知 DeviceIoControl 调用我们正在读取一个 CD 并且我们正在读取一个扇区,该扇区位于光盘上。 (记住 CD 扇区与 HD 扇区略有不同;更多关于 后者。)
现在我们通过 DeviceIoControl 读取该扇区。如果成功
然后我们检索刚刚读取的扇区数据。将扇区数据放入TrackData中合适的位置
锯齿状数组。重复轨道中的所有扇区。
重复 CD 上的所有曲目。
使用 kerenl32 中的 CloseHandle 关闭驱动器的句柄。
// this functions reads binary audio data from a cd and stores it in a jagged array called TrackData // it uses only low level file io calls to open and read the Table of Content and then the binary 'music' data sector by sector // as discovered from the table of content // it also writes it to a binary file called tracks with not extension // this file can be read by any decent hex editor void readcd() { bool TocValid = false; IntPtr cdHandle = IntPtr.Zero; CDROM_TOC Toc = null; int track, StartSector, EndSector; BinaryWriter bw; bool CDReady; uint uiTrackCount, uiTrackSize, uiDataSize; int i; uint BytesRead, Dummy; char Drive = (char)cmbDrives.Text[0]; TRACK_DATA td; int sector; byte[] SectorData; IntPtr pnt; Int64 Offset; btnStart.Enabled = false; Dummy = 0; BytesRead = 0; CDReady = false; Toc = new CDROM_TOC(); IntPtr ip = Marshal.AllocHGlobal((IntPtr)(Marshal.SizeOf(Toc))); Marshal.StructureToPtr(Toc, ip, false); // is it a cdrom drive DriveTypes dt = GetDriveType(Drive + ":\"); if (dt == DriveTypes.DRIVE_CDROM) { // get a Handle to control the drive with cdHandle = CreateFile("\\.\" + Drive + ':', GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); CDReady = DeviceIoControl(cdHandle, IOCTL_STORAGE_CHECK_VERIFY, IntPtr.Zero, 0, IntPtr.Zero, 0, ref Dummy, IntPtr.Zero) == 1; if (!CDReady) { MessageBox.Show("Drive Not Ready", "Drive Not Ready", MessageBoxButtons.OK); } else { uiTrackCount = 0; // is the Table of Content valid? TocValid = DeviceIoControl(cdHandle, IOCTL_CDROM_READ_TOC, IntPtr.Zero, 0, ip, (uint)Marshal.SizeOf(Toc), ref BytesRead, IntPtr.Zero) != 0; //fetch the data from the unmanaged pointer back to the managed structure Marshal.PtrToStructure(ip, Toc); if (!TocValid) { MessageBox.Show("Invalid Table of Content ", "Invalid Table of Content ", MessageBoxButtons.OK); } else { // really only nescary if there are un-useable tracks uiTrackCount = Toc.LastTrack; //for (i = Toc.FirstTrack - 1; i < Toc.LastTrack; i++) //{ // if (Toc.TrackData[i].Control == 0) // uiTrackCount++; //} // create a jagged array to store the track data TrackData = new byte[uiTrackCount][]; // read all the tracks for (track = 1; track <= uiTrackCount; track++)//uiTrackCount; track++) { Offset = 0;// used to store Sectordata into trackdata label1.Text = "Reading Track" + track.ToString() + " of " + uiTrackCount.ToString(); ; Application.DoEvents(); // create a binary writer to write the track data bw = new BinaryWriter(File.Open(Application.StartupPath + "\Track" + track.ToString (), FileMode.Create)); //The CDROM_TOC-structure contains the FirstTrack (1) and the LastTrack (max. track nr). CDROM_TOC::TrackData[0] contains info of the //first track on the CD. Each track has an address. It represents the track's play-time using individual members for the hour, minute, //second and frame. The "frame"-value (Address[3]) is given in 1/75-parts of a second -> Remember: 75 frames form one second and one //frame occupies one sector. //Find the first and last sector of the track td = Toc.TrackData[track - 1]; // minutes Seconds fractional seconds 150 bytes is the 2 second lead in to track 1 StartSector = (td.Address_1 * 60 * 75 + td.Address_2 * 75 + td.Address_3) - 150; td = Toc.TrackData[track]; EndSector = (td.Address_1 * 60 * 75 + td.Address_2 * 75 + td.Address_3) - 151; progressBar1.Minimum = StartSector; progressBar1.Maximum = EndSector; uiTrackSize = (uint)(EndSector - StartSector) * CB_AUDIO;//CB_AUDIO==2352 // how big is the track uiDataSize = (uint)uiTrackSize; //Allocate for the track TrackData[track - 1] = new byte[uiDataSize]; SectorData = new byte[CB_AUDIO * NSECTORS]; // read all the sectors for this track for (sector = StartSector; (sector < EndSector); sector += NSECTORS) { Debug.Print(sector.ToString("X2")); RAW_READ_INFO rri = new RAW_READ_INFO();// contains info about the sector to be read rri.TrackMode = TRACK_MODE_TYPE.CDDA; rri.SectorCount = (uint)1; rri.DiskOffset = sector * CB_CDROMSECTOR; //get a pointer to the structure Marshal.StructureToPtr(rri, ip, false); // allocate an unmanged pointer to hold the data read from the disc int size = Marshal.SizeOf(SectorData[0]) * SectorData.Length; pnt = Marshal.AllocHGlobal(size); //Sector data is a byte array to hold data from each sector data // initiallize it to all zeros SectorData.Initialize(); // read the sector i = DeviceIoControl(cdHandle, IOCTL_CDROM_RAW_READ, ip, (uint)Marshal.SizeOf(rri), pnt, (uint)NSECTORS * CB_AUDIO, ref BytesRead, IntPtr.Zero); if (i == 0) { MessageBox.Show("Bad Sector Read", "Bad Sector Read from sector " + sector.ToString("X2"), MessageBoxButtons.OK); break; } progressBar1.Value = sector; // return the pointers to their respective managed data sources Marshal.PtrToStructure(ip, rri); Marshal.Copy(pnt, SectorData, 0, SectorData.Length); Marshal.FreeHGlobal(pnt); Array.Copy(SectorData, 0, TrackData[track - 1], Offset, BytesRead); Offset += BytesRead; } // write the binary data nad then close it bw.Write(TrackData[track - 1]); bw.Close(); } //unlock PREVENT_MEDIA_REMOVAL pmr = new PREVENT_MEDIA_REMOVAL(); pmr.PreventMediaRemoval = 0; ip = Marshal.AllocHGlobal((IntPtr)(Marshal.SizeOf(pmr))); Marshal.StructureToPtr(pmr, ip, false); DeviceIoControl(cdHandle, IOCTL_STORAGE_MEDIA_REMOVAL, ip, (uint)Marshal.SizeOf(pmr), IntPtr.Zero, 0, ref Dummy, IntPtr.Zero); Marshal.PtrToStructure(ip, pmr); Marshal.FreeHGlobal(ip); } } } //Close the CD Handle CloseHandle(cdHandle); ConvertToWav(); }
ConvertToWav 方法:
- 初始化 .wav 所需的四个 ChunkIds header。
然后我们将三个主要chunk的各个部分初始化为
代表PCM、Stereo、44100 Samples per second等方面
代表真正的 CD 数据。接下来,我们遍历锯齿状
中表示的所有轨道 数组 TrackData.创建一个名为 "Track(x).wav" 的文件,并使用
return 它的句柄 在 Kernel32.CreateFile.构建 Header.
- 添加 "Music" 数据。
使用 Kernel32 中的 WriteFile 写入文件。
如果成功则继续。
我们刷新 WriteFile 中使用的所有缓冲区。
然后我们使用 CloseHandle 关闭文件。
Return 并进行下一曲,直到全部完成。
现在我们去播放 wav 文件并幸灾乐祸我们有多熟
// this procedure tacks the biary data stored in the jagged array called TraackData // and, using low level file io functions) writes it out as a .wav file called trackx.wav private void ConvertToWav() { int i, j, k, track, tracks; byte[] b; char[] riffchunk ={ 'R', 'I', 'F', 'F' }; char[] wavechunk ={ 'W', 'A', 'V', 'E' }; char[] datachunk ={ 'd', 'a', 't', 'a' }; char[] fmtchunk ={ 'f', 'm', 't', ' ' }; Int32 riffsize, datasize, fmtsize, extrabits; Int32 DI, SampleRate, ByteRate; uint BytesWritten; Int16 BlockAlign, Format, NumChannels, BitsPerSample; Byte[] Image; IntPtr FileHandle; Format = 1; // PCM NumChannels = 2;// Stereo SampleRate = 44100;// 44100 Samples per secon BitsPerSample = 16; // 16 bits per sample ByteRate = SampleRate * NumChannels * BitsPerSample / 8; BlockAlign = 4; fmtsize = 0x12;// size of the 'fmt ' chunk is 18 bytes // get the number of tarcks stoerd in track data tracks = TrackData.GetUpperBound(0); // setup the progressbar progressBar1.Maximum = tracks; progressBar1.Minimum = 0; // do all the tracks for (track = 0; track <= tracks; track++) { DI = 0;//IDI is an index into the Image array where the next chunk of data will be stored progressBar1.Value = track; label1.Text = "Writeing Track " + (track + 1).ToString() + ".wav"; Application.DoEvents(); // Create a File called trackx.wav and return a handle to it FileHandle=CreateFile(Application.StartupPath + "\Track" + (track + 1).ToString() + ".wav",GENERIC_WRITE,0,IntPtr.Zero ,OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero ); // Wav file format is notthe subject of this project .. . // suffice it to say that at minimum there is a Header which is followed by the PCM, Stereo , 44100 Hz Sample rate binary data // for more info on Wav format plese visit: //http://www.sonicspot.com/guide/wavefiles.html //Start prepareing the RIFF header // how big the the 'music' binary data datasize = TrackData[track].Length; //build the header riffsize = datasize; riffsize += 4;//RIFFSize riffsize += 4;//WAVE riffsize += 4;//fmt riffsize += fmtsize; riffsize += 4;// DATA riffsize += 4;//datasize extrabits = 0; // build the image Image = new Byte[riffsize + 8];// riffchunk + riffsize b = Encoding.ASCII.GetBytes(riffchunk); Array.Copy(b, 0, Image, DI, 4); DI += 4; b = BitConverter.GetBytes(riffsize); if (!BitConverter.IsLittleEndian) Array.Reverse(b); Array.Copy(b, 0, Image, DI, 4); DI += 4; b = Encoding.ASCII.GetBytes(wavechunk); Array.Copy(b, 0, Image, DI, 4); DI += 4; b = Encoding.ASCII.GetBytes(fmtchunk); if (!BitConverter.IsLittleEndian) Array.Reverse(b); Array.Copy(b, 0, Image, DI, 4); DI += 4; b = BitConverter.GetBytes(fmtsize); if (!BitConverter.IsLittleEndian) Array.Reverse(b); Array.Copy(b, 0, Image, DI, 4); DI += 4; b = BitConverter.GetBytes(Format); if (!BitConverter.IsLittleEndian) Array.Reverse(b); Array.Copy(b, 0, Image, DI, 2); DI += 2; b = BitConverter.GetBytes(NumChannels); if (!BitConverter.IsLittleEndian) Array.Reverse(b); Array.Copy(b, 0, Image, DI, 2); DI += 2; b = BitConverter.GetBytes(SampleRate); if (!BitConverter.IsLittleEndian) Array.Reverse(b); Array.Copy(b, 0, Image, DI, 4); DI += 4; b = BitConverter.GetBytes(ByteRate); if (!BitConverter.IsLittleEndian) Array.Reverse(b); Array.Copy(b, 0, Image, DI, 4); DI += 4; b = BitConverter.GetBytes(BlockAlign); if (!BitConverter.IsLittleEndian) Array.Reverse(b); Array.Copy(b, 0, Image, DI, 2); DI += 2; b = BitConverter.GetBytes(BitsPerSample); if (!BitConverter.IsLittleEndian) Array.Reverse(b); Array.Copy(b, 0, Image, DI, 2); DI += 2; b = BitConverter.GetBytes(extrabits); if (!BitConverter.IsLittleEndian) Array.Reverse(b); Array.Copy(b, 0, Image, DI, 2); DI += 2; b = Encoding.ASCII.GetBytes(datachunk); Array.Copy(b, 0, Image, DI, 4); DI += 4; b = BitConverter.GetBytes(datasize); if (!BitConverter.IsLittleEndian) Array.Reverse(b); Array.Copy(b, 0, Image, DI, 4); DI += 4; // add the digital 'music' data retrieved earler Array.Copy(TrackData[track], 0, Image, DI, TrackData[track].Length); // write the binary file - trackx.wav i = WriteFile(FileHandle, Image, (uint)Image.Length, out BytesWritten, IntPtr.Zero); //if successful then // flush all buffers used in the low level write operation // then close the file if(i!= 0) { //Flush the file buffers to force writing of the data. i = FlushFileBuffers(FileHandle); //Close the file. i = CloseHandle(FileHandle); } // the wave file now exists (created by reading the CD and can be playedby most wav players Image = null; progressBar1.Value = track; } }
文件比较
此方法在检查所有字节是否相等之前进行验证。
- 当两个文件的长度不同时,它们不可能是同一个文件,false 将被 returned
之后,该方法比较所有字节,return如果所有字节都等于
,则成功private bool FileCompare(string file1, string file2) { int file1byte; int file2byte; FileStream fs1; FileStream fs2; // Open the two files. fs1 = new FileStream(file1, FileMode.Open); fs2 = new FileStream(file2, FileMode.Open); // Check the file sizes. If they are not the same, the files // are not the same. if (fs1.Length != fs2.Length) { // Close the file fs1.Close(); fs2.Close(); // Return false to indicate files are different return false; } // Read and compare a byte from each file until either a // non-matching set of bytes is found or until the end of // file1 is reached. do { // Read one byte from each file. file1byte = fs1.ReadByte(); file2byte = fs2.ReadByte(); } while ((file1byte == file2byte) && (file1byte != -1)); // Close the files. fs1.Close(); fs2.Close(); // Return the success of the comparison. "file1byte" is // equal to "file2byte" at this point only if the files are // the same. return ((file1byte - file2byte) == 0); }
链接: