1
votes

When I change the waveFormat of a wav file the "data" identifier shifts to the right. The data identifier which represents the beginning of the da chunk has to be at the 0x24 position but after I change the waveFormat with the NAudio library it shiffts to the 0x26 position.

WAV format

enter image description here

This is the code I use to change the waveFormat:

private void TurnTo16bitsAudio(string path)
        {
            NAudio.Wave.WaveFileReader wave = new NAudio.Wave.WaveFileReader(@path);
            System.Console.WriteLine(wave.WaveFormat);
            if (wave.WaveFormat.BitsPerSample >= 16 && wave.WaveFormat.Channels<2)
            {
                wave.Dispose();
                return;
            }

            var newFormat = new WaveFormat(44100, 16, 1);
            var conversionStream = new WaveFormatConversionStream(newFormat, wave);
            WaveFileWriter.CreateWaveFile(@path + '1', conversionStream);
            wave.Dispose();
            wave = new NAudio.Wave.WaveFileReader(@path + '1');
            conversionStream = new WaveFormatConversionStream(newFormat, wave);
            WaveFileWriter.CreateWaveFile(@path, conversionStream);
            wave.Dispose();
            conversionStream.Dispose();
            System.IO.File.Delete(@path + '1');

        }

There is any possibility to change the wav header with NAudio or to change the waveFormat without shiffting the "data" identifier

1

1 Answers

1
votes

Method Serialize in WaveFormat class never outputs 0x10 in Subchunk1Size field. Minimum value is 0x12.

public virtual void Serialize(BinaryWriter writer)
{
    writer.Write((int)(18 + extraSize)); // wave format length
    writer.Write((short)Encoding);
    writer.Write((short)Channels);
    writer.Write((int)SampleRate);
    writer.Write((int)AverageBytesPerSecond);
    writer.Write((short)BlockAlign);
    writer.Write((short)BitsPerSample);
    writer.Write((short)extraSize);
}

Additional 2 bytes are for ExtraParamSize which is ignored when encoding is set to WaveFormatEncoding.Pcm (its value == 0). This is related to change in the WAV format done by Microsoft (Windows 2000 and later) when WAVEFORMATEX was introduced.

Here you can find answers on subchunk1size 16/18 different size question: C++ read wav file, subchunk1size = 18

Detailed information about WAVEFORMATEX structure: https://msdn.microsoft.com/library/windows/hardware/ff538799

If you really want to force creation of wav header without extraSize field you can extend WaveFormat class.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 2)]
class CustomWaveFormat : WaveFormat
{
    public CustomWaveFormat(int rate, int bits, int channels) 
        : base(rate, bits, channels)
    {
        extraSize = 0;
    }

    public override void Serialize(BinaryWriter writer)
    {
        writer.Write((int)16); // wave format length
        writer.Write((short)Encoding);
        writer.Write((short)Channels);
        writer.Write((int)SampleRate);
        writer.Write((int)AverageBytesPerSecond);
        writer.Write((short)BlockAlign);
        writer.Write((short)BitsPerSample);
    }
}

Usage:

...
    var newFormat = new CustomWaveFormat(44100, 16, 1);

    using (var conversionStream = new WaveFormatConversionStream(newFormat, waveFileReader))
    {
        WaveFileWriter.CreateWaveFile("example_new.wav", conversionStream);
    }
...