我写了一段代码,将wav文件刻录到音频CD中。 它可以正常工作,但是,在执行验证的最后一步,它在某些DVD驱动器上失败。这些文件实际上已刻录到CD中,可以正常播放。但是验证似乎无缘无故。我可以关闭验证。但是,我更喜欢编写另一个函数来手动检查已刻录的文件并验证它们是否是wav文件的实际结果。我能够这样做来刻录数据CD。但是对于音频CD,由于它会将它们转换为光盘上的cda文件,因此我无法进行比较。关于如何使用C#进行验证的任何建议?基本上,让我们假设我有一个音频CD,其中包含一些.cda文件,并且我想确保它们是原始wav文件中实际转换的文件。我确实知道cda文件只是占位符,我只是不知道如何从其中获取wav文件(如果可能)与原始wav文件进行比较。
答案 0 :(得分:1)
将cda文件转换为wav
将cda转换为wav文件并不是那么容易。
您必须使用一些非托管内存和指针从cd读取数据。
ReadCD方法:
readcd过程验证该驱动器是cdrom驱动器
然后它通过调用CreateFile in
获得驱动器的句柄。
内核32。
接下来,我们使用该句柄查看驱动器是否已准备好使用
进行读取。
kerenl32中的DeviceIoControl。
如果驱动器已准备就绪,那么我们将查看其是否具有有效的目录 (以下称为TOC)再次使用DeviceIoControl。
如果目录有效,那么我们接下来使用DeviceIoControl读取目录。
使用TOC,我们确定CD上可能有多少曲目(否
在此处归档IO;我们已经有了TOC)。然后在迭代中
我们进行的所有曲目。
我们创建了一个二进制写入器,用于写入二进制文件。
我们将TOC跟踪数据转换为称为
的kernel32结构
TRACK_DATA。
使用该结构,我们可以确定哪个部门持有 该曲目的开头。
l扇区将比l扇区的起始扇区少一个扇区
下一首。旁注:有很多指向结构和字节的指针
数组,因此在之间进行很多来回转换
他们。
通过减去开始,磁道大小以扇区数表示
从头开始。
现在,我们遍历此轨道中的所有扇区。
我们创建要在
中使用的kernel32 RAW_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方法:
然后我们将三个主要块的各个部分初始化为
表示PCM,立体声,每秒44100个样本和其他方面
代表真实的CD数据。
接下来,我们遍历锯齿状的所有轨道
数组TrackData。
创建一个名为“ Track(x).wav”的文件,并使用
返回其句柄
Kernel32中的CreateFile。
构建标题。
使用来自Kernel32的WriteFile写入文件。
如果成功则继续。
我们刷新WriteFile中使用的所有缓冲区。
然后我们使用CloseHandle关闭文件。
返回并执行下一首曲目,直到全部完成。
现在我们开始播放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;
}
}
文件比较
在检查所有字节是否相等之前,此方法具有有效性。
此后,该方法将比较所有字节,如果所有字节均相等,则返回成功
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);
}
链接: