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

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

1 Ответ

1 голос
/ 08 мая 2019

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

Конвертировать файл cda в файл wav не так просто.

Для чтения данных необходимо использовать некоторую неуправляемую память и указательиз CD.


Метод ReadCD:

  • Процедура readcd проверяет, является ли дисковод CDROM

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

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

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

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

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

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

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

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

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

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

  • Теперь мы перебираем все сектора в этом треке.

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

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

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

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

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

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

  • Закройте ручкук диску с помощью 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:

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

  • Далее мы перебираем все дорожки, как представлено в неровном
    массиве 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);
     }
    

Ссылки:

...