Пытаюсь прочитать 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-файл в следующий формат:
После чего дополнительные данные исчезли из этого файла и я смог успешно прочитать все данные.
Комментариев нет:
Отправить комментарий