Создание файла .wav в С#

В качестве предлога для изучения С#, я пытаюсь закодировать простой проект: создание аудиофайлов. Для начала я хочу убедиться, что могу писать файлы, соответствующие формату WAVE. Я исследовал формат в Интернете (например, здесь), но всякий раз, когда я пытаюсь воспроизвести файл, он не открывается корректно. Вот мой код. Что-то отсутствует или неверно?

uint numsamples = 44100;
ushort numchannels = 1;
ushort samplelength = 1; // in bytes
uint samplerate = 22050;

FileStream f = new FileStream("a.wav", FileMode.Create);
BinaryWriter wr = new BinaryWriter(f);

wr.Write("RIFF");
wr.Write(36 + numsamples * numchannels * samplelength);
wr.Write("WAVEfmt ");
wr.Write(16);
wr.Write((ushort)1);
wr.Write(numchannels);
wr.Write(samplerate);
wr.Write(samplerate * samplelength * numchannels);
wr.Write(samplelength * numchannels);
wr.Write((ushort)(8 * samplelength));
wr.Write("data");
wr.Write(numsamples * samplelength);

// for now, just a square wave
Waveform a = new Waveform(440, 50);

double t = 0.0;
for (int i = 0; i < numsamples; i++, t += 1.0 / samplerate)
{
    wr.Write((byte)((a.sample(t) + (samplelength == 1 ? 128 : 0)) & 0xff));
}

Ответ 1

Основная проблема:

BinaryWriter.Write(string) записывает строку, для которой имеет префикс длиной для BinaryReader, чтобы прочитать ее. Он не предназначен для использования, как ваш случай. Вам нужно просто написать байты вместо использования BinaryWriter.Write(string).

Что вы должны сделать:

Преобразуйте строку в байты, а затем напрямую напишите байты.

byte[] data = System.Text.Encoding.ASCII.GetBytes("RIFF");
binaryWriter.Write(data);

или сделать его одной строкой:

binaryWriter.Write(System.Text.Encoding.ASCII.GetBytes("RIFF"));

Также могут возникнуть другие проблемы, такие как целые числа, которые вы пишете, могут быть не того размера, который требуется. Вы должны тщательно их проверить.

Что касается endianess, ссылка, которую вы ставите, указывает, что данные находятся в little-endian и BinaryWriter использует little-endian, поэтому это не должно быть проблемой.

Ответ 2

Самый простой способ, который вы можете просто изменить:

wr.Write("RIFF");

в

wr.Write("RIFF".ToArray());

Написав строку в двоичном файле, она будет содержать длину строки, чтобы впоследствии ее десериализовать обратно в строку. В этом случае вы просто хотите, чтобы четыре байта записывались как четыре байта, и преобразование их в массив char будет делать именно это.

Ответ 3

Мне не хватает данных WAV, но попробуйте заменить часть кода, в которой вы создаете заголовок с помощью этого кода (замените его соответствующим образом):

wr.Write(Encoding.ASCII.GetBytes("RIFF"));
wr.Write(0);
wr.Write(Encoding.ASCII.GetBytes("WAVE"));
wr.Write(Encoding.ASCII.GetBytes("fmt "));
wr.Write(18 + (int)(numsamples * samplelength));
wr.Write((short)1); // Encoding
wr.Write((short)numchannels); // Channels
wr.Write((int)(samplerate)); // Sample rate
wr.Write((int)(samplerate * samplelength * numchannels)); // Average bytes per second
wr.Write((short)(samplelength * numchannels)); // block align
wr.Write((short)(8 * samplelength)); // bits per sample
wr.Write((short)(numsamples * samplelength)); // Extra size
wr.Write("data");