#c_sharp #аудио #wav
Пытаюсь прочитать Wav-файл. Считываю заголовок. Так получается, что размер данных (Subchunk2Size, для примера, он равен 178) всегда не кратен размеру блока (BlockAlign, равен 4). Количество каналов - 2. Если мы будем по очереди считывать по 2 байта из данных, то в конце концов у правого канала будет на 1 значение меньше. Почему так? И как правильно в таком случае разделить данные между двумя каналами? Класс, который читает Wav-файл (переделанный пример, взятый отсюда): public class Parser { public string FilePath { get; private set; } public Parser(string filePath) { FilePath = filePath; } public ParsingResult Parse() { var header = new WavHeader(); byte[] data; using (var fileStream = new FileStream(FilePath, FileMode.Open, FileAccess.Read)) { using (var reader = new BinaryReader(fileStream)) { header.ChunkId = reader.ReadInt32(); header.ChunkSize = reader.ReadInt32(); header.Format = reader.ReadInt32(); header.Subchunk1Id = reader.ReadInt32(); header.Subchunk1Size = reader.ReadInt32(); header.AudioFormat = reader.ReadInt16(); header.NumChannels = reader.ReadInt16(); header.SampleRate = reader.ReadInt32(); header.ByteRate = reader.ReadInt32(); header.BlockAlign = reader.ReadInt16(); header.BitsPerSample = reader.ReadInt16(); if (header.Subchunk1Size == 18) { // Read extra values (Сюда при чтении моего файла не заходит) header.FmtExtraSize = reader.ReadInt16(); reader.ReadBytes(header.FmtExtraSize); } header.Subchunk2Id = reader.ReadInt32(); header.Subchunk2Size = reader.ReadInt32(); data = reader.ReadBytes(header.Subchunk2Size); } } var result = new ParsingResult(header, data); return result; } } Класс WavHeader: public struct WavHeader { // WAV-формат начинается с RIFF-заголовка: // Содержит символы "RIFF" в ASCII кодировке // (0x52494646 в big-endian представлении) public int ChunkId { get; set; } // 36 + subchunk2Size, или более точно: // 4 + (8 + subchunk1Size) + (8 + subchunk2Size) // Это оставшийся размер цепочки, начиная с этой позиции. // Иначе говоря, это размер файла - 8, то есть, // исключены поля chunkId и chunkSize. public int ChunkSize { get; set; } // Содержит символы "WAVE" // (0x57415645 в big-endian представлении) public int Format { get; set; } // Формат "WAVE" состоит из двух подцепочек: "fmt " и "data": // Подцепочка "fmt " описывает формат звуковых данных: // Содержит символы "fmt " // (0x666d7420 в big-endian представлении) public int Subchunk1Id { get; set; } // 16 для формата PCM. // Это оставшийся размер подцепочки, начиная с этой позиции. public int Subchunk1Size { get; set; } // Аудио формат, полный список можно получить здесь http://audiocoding.ru/wav_formats.txt // Для PCM = 1 (то есть, Линейное квантование). // Значения, отличающиеся от 1, обозначают некоторый формат сжатия. public short AudioFormat { get; set; } // Количество каналов. Моно = 1, Стерео = 2 и т.д. public short NumChannels { get; set; } // Частота дискретизации. 8000 Гц, 44100 Гц и т.д. public int SampleRate { get; set; } // sampleRate * numChannels * bitsPerSample/8 public int ByteRate { get; set; } // numChannels * bitsPerSample/8 // Количество байт для одного сэмпла, включая все каналы. public short BlockAlign { get; set; } // Так называемая "глубиная" или точность звучания. 8 бит, 16 бит и т.д. public short BitsPerSample { get; set; } public int FmtExtraSize { get; set; } // Подцепочка "data" содержит аудио-данные и их размер. // Содержит символы "data" // (0x64617461 в big-endian представлении) public int Subchunk2Id { get; set; } // numSamples * numChannels * bitsPerSample/8 // Количество байт в области данных. public int Subchunk2Size { get; set; } }
Ответы
Ответ 1
Итак, по совету пользователя KoVadim я заглянул в hex-редактор, и к моему удивлению, содержимое wav-файла отличалось от того, чего я ожидал. Привожу содержимое wav-файла: Если верить этому источнику и взглянуть на наш файл, то мы увидим, что в поле AudioFormat(на картинке отмечено синим) занесено значение 1, т.е. это PCM (формат без сжатия). Читаем дальше. После того, как прочитали поле BitsPerSample (на картинке зеленым) ожидается, что следующие 4 байта будут data (потому что в PCM, если верить источнику, не должно быть никаких дополнительных данных между BitsPerSample и data), но мы видим, что до поля data (подчеркнуто красным) идет дополнительная информация о треке, чего быть не должно. Не знаю, с чем именно связано это. Буду очень признателен, если мне объяснят, как правильно прочитать эти дополнительные данные и почему они лежат в данном файле. В итоге я решил проблему следующим способом: с помощью популярного аудио-редактора Audacity я перекодировал данный Wav-файл в следующий формат: После чего дополнительные данные исчезли из этого файла и я смог успешно прочитать все данные.Ответ 2
потому что в PCM, если верить источнику, не должно быть никаких дополнительных данных между BitsPerSample и data Вероятно, слишком поздно, но источник врет. Во-первых, PCM - это способ кодирования, а не формат файла (формат называется RIFF). Во-вторых, согласно спецификации данного формата, он может расширяться в последующих версиях добавлением новых блоков данных (в любом месте между старыми блоками), поэтому приложения для его считывания должны пропускать неизвестные им блоки, а не падать с ошибкой при их появлении. В данном случае, между заголовком файла и блоком DATA содержится блок INFO, который содержит текстовые сведения о композиции (отлично заметные кракозябры в HEX-редакторе). Таким образом, правильный алгоритм считывания блоков RIFF-файла выглядит как-то так: Считать SubchunkID и SubchunkSize Если SubchunkID - не то, что ожидалось, пропустить SubchunkSize байтов и вернуться на шаг 1 Иначе, считать и обработать массив байтов, равный SubchunkSize Информацию о формате и ссылки на нормальную документацию можно найти здесь: Audio File Format Specifications
Комментариев нет:
Отправить комментарий