Как проверить, что файл cda на Audio CD является результатом файла wav в C#

Я написал фрагмент кода для записи файлов wav на аудио компакт-диск. Работает нормально, однако на последнем шаге при выполнении проверки на некоторых DVD-приводах происходит сбой. Файлы фактически записываются на компакт-диск, и их можно воспроизводить без проблем. Но проверка, кажется, не проходит без причины. Я могу отключить проверку. Однако я предпочитаю написать другую функцию, которая вручную проверяет записанные файлы и подтверждает, что они являются фактическим результатом файлов wav. Я смог сделать это для записи компакт-диска с данными. Но для Audio CD, поскольку он преобразует их в файлы cda на диске, я не могу их сравнивать. Любое предложение о том, как проверить их с помощью С#? В общем, давайте предположим, что у меня есть аудио компакт-диск с несколькими файлами .cda, и я хочу убедиться, что они действительно преобразованы из исходных файлов wav. Я знаю, что файлы cda - это просто заполнители, я просто не знаю, как получить из них файлы wav (если это возможно) для сравнения с исходными файлами wav.


person shahram kalantari    schedule 07.05.2019    source источник
comment
Можно ли преобразовать файл cda в файл wav?   -  person programmer444    schedule 07.05.2019
comment
@programmer444 да, это так. Если я смогу преобразовать его обратно в wav на C#, а затем сравнить с исходным wav, я думаю, этого достаточно.   -  person shahram kalantari    schedule 08.05.2019


Ответы (1)


Преобразование файла cda в wav

Не так-то просто преобразовать cda в wav-файл.

Вы должны использовать некоторую неуправляемую память и указатель для чтения данных с компакт-диска.


Метод чтения компакт-диска:

  • Процедура readcd проверяет, что диск является дисководом компакт-дисков.

  • Затем он получает дескриптор диска с помощью вызова CreateFile в
    Kernel32.

  • Затем мы используем этот дескриптор, чтобы проверить, готов ли диск к чтению, используя
    DeviceIoControl в kerenl32.

  • Если диск готов, мы снова смотрим, есть ли у него действительное оглавление (далее — TOC), используя DeviceIoControl.

  • Если TOC действителен, мы затем читаем TOC с помощью DeviceIoControl.

  • Используя оглавление, мы определяем, сколько треков может быть на компакт-диске (здесь нет файлового ввода-вывода; у нас уже есть оглавление). Затем в итерации,
    просматривая все треки, мы продолжаем.

  • Мы создаем двоичный модуль записи для использования при записи двоичных файлов.

  • Мы передаем данные дорожки TOC в структуру ядра32 с именем
    TRACK_DATA.

  • Используя эту структуру, мы можем определить, какой сектор содержит начало этой дорожки.

  • И сектор l будет на один сектор меньше, чем начальный сектор следующей дорожки. Примечание: существует множество указателей на структуры и массивы байтов, поэтому между ними также часто выполняется преобразование
    туда и обратно.

  • Размер дорожки выражается в количестве секторов путем вычитания start
    из end.

  • Теперь проходим по всем секторам этой дорожки.

  • Мы создаем структуру RAW_READ_INFO ядра32, которая будет использоваться в вызове
    DeviceIoControl для чтения сектора.

  • Структура информирует вызов DeviceIoControl о том, что мы читаем компакт-диск и что мы читаем один сектор, в котором он находится на диске. (Помните, что сектора CD немного отличаются от секторов HD; подробнее об этом последнем.)

  • Теперь читаем этот сектор через DeviceIoControl. Если это было успешно
    , мы извлекаем только что прочитанные данные сектора.

  • Поместите данные сектора в соответствующее место в неровном массиве TrackData
    .

  • Повторите для всех секторов дорожки.

  • Повторите для всех дорожек на компакт-диске.

  • Закройте дескриптор диска с помощью CloseHandle в kerenl32.

     // 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:

  • Инициализируйте четыре ChunkId, необходимые для заголовка .wav.
  • Затем мы инициализируем различные части трех основных фрагментов,
    представляя PCM, Stereo, 44100 выборок в секунду и другие аспекты,
    которые представляют настоящие данные компакт-диска.

  • Затем мы перебираем все дорожки, представленные в массиве TrackData с зубчатой ​​кромкой
    .

  • Создайте файл с именем "Track(x).wav" и верните ему дескриптор с помощью
    CreateFile в Kernel32.

  • Создайте заголовок.

  • Добавьте данные «Музыка».
  • Запишите файл с помощью WriteFile из Kernel32.

  • Продолжайте, если это было успешно.

  • Мы очищаем все буферы, используемые в 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;
    
        }
    
    }
    

Сравнить файлы

Этот метод имеет валидацию перед проверкой всех байтов на равенство.

  • Когда длина двух файлов не одинакова, невозможно, чтобы они были одним и тем же файлом, и будет возвращено значение false.
  • После этого метод сравнивает все байты и возвращает успех, если все они равны

    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);
     }
    

Ссылки:

person programmer444    schedule 08.05.2019