Страницы

Поиск по вопросам

вторник, 12 марта 2019 г.

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


Ответ

Итак, по совету пользователя KoVadim я заглянул в hex-редактор, и к моему удивлению, содержимое wav-файла отличалось от того, чего я ожидал.
Привожу содержимое wav-файла:
Если верить этому источнику и взглянуть на наш файл, то мы увидим, что в поле AudioFormat(на картинке отмечено синим) занесено значение 1, т.е. это PCM (формат без сжатия). Читаем дальше. После того, как прочитали поле BitsPerSample (на картинке зеленым) ожидается, что следующие 4 байта будут data (потому что в PCM, если верить источнику, не должно быть никаких дополнительных данных между BitsPerSample и data), но мы видим, что до поля data (подчеркнуто красным) идет дополнительная информация о треке, чего быть не должно. Не знаю, с чем именно связано это. Буду очень признателен, если мне объяснят, как правильно прочитать эти дополнительные данные и почему они лежат в данном файле.
В итоге я решил проблему следующим способом: с помощью популярного аудио-редактора Audacity я перекодировал данный Wav-файл в следующий формат:

После чего дополнительные данные исчезли из этого файла и я смог успешно прочитать все данные.

Комментариев нет:

Отправить комментарий