4

I need to convert a wav file to 8000Hz 16Bit Mono Wav. I already have a code, which works well with NAudio library, but I want to use MemoryStream instead of temporary file.

using System.IO;
using NAudio.Wave;

    static void Main()
    {
        var input = File.ReadAllBytes("C:/input.wav");
        var output = ConvertWavTo8000Hz16BitMonoWav(input);
        File.WriteAllBytes("C:/output.wav", output);
    }

    public static byte[] ConvertWavTo8000Hz16BitMonoWav(byte[] inArray)
    {
        using (var mem = new MemoryStream(inArray))
        using (var reader = new WaveFileReader(mem))
        using (var converter = WaveFormatConversionStream.CreatePcmStream(reader))
        using (var upsampler = new WaveFormatConversionStream(new WaveFormat(8000, 16, 1), converter))
        {
            // todo: without saving to file using MemoryStream or similar
            WaveFileWriter.CreateWaveFile("C:/tmp_pcm_8000_16_mono.wav", upsampler);
            return File.ReadAllBytes("C:/tmp_pcm_8000_16_mono.wav");
        }
    }
Licht
  • 1,079
  • 1
  • 12
  • 27
Sergey Malyutin
  • 1,484
  • 2
  • 20
  • 44
  • You can use one of the constructors of `WaveFileWriter` that takes a stream for argument. Taken from [NAudio source code](https://github.com/naudio/NAudio/blob/master/NAudio/Wave/WaveOutputs/WaveFileWriter.cs#LC63) – doomgg Mar 06 '16 at 08:50
  • @bob1024 I already tried it, but there is no param for converter... or probably I am missing something – Sergey Malyutin Mar 06 '16 at 08:52
  • You can specify the rate, bits, and channels in a `WaveFormat` variable and pass it to the constructor of `WaveFileWriter` – doomgg Mar 06 '16 at 08:55
  • @bob1024, Yes I can, it will be like using (var mem = new MemoryStream(inArray)) { var waveFileWriter = new WaveFileWriter(mem, new WaveFormat(8000, 16, 1)); } But what is next? – Sergey Malyutin Mar 06 '16 at 08:58
  • I think you could transfer audio samples from the reader to the writer with `WaveFileReader.ReadNextSampleFrame` and `WaveFileWriter.WriteSample`. I don't see another way. – doomgg Mar 06 '16 at 09:22
  • @bob1024 I also tried using (var m = new MemoryStream()) { upsampler.CopyTo(m); return m.ToArray(); } instead of tmp file. The result file looks very similar, but it does not have RIF header at the beginning for some reason... – Sergey Malyutin Mar 06 '16 at 09:34
  • to convert to WAV (M4A, AAC) file, with ***attributes A-Law, 8000Hz, 64kbps, mono*** ? – Kiquenet Feb 15 '23 at 17:02

2 Answers2

3

Not sure if this is the optimal way, but it works...

    public static byte[] ConvertWavTo8000Hz16BitMonoWav(byte[] inArray)
    {
        using (var mem = new MemoryStream(inArray))
        {
            using (var reader = new WaveFileReader(mem))
            {
                using (var converter = WaveFormatConversionStream.CreatePcmStream(reader))
                {
                    using (var upsampler = new WaveFormatConversionStream(new WaveFormat(8000, 16, 1), converter))
                    {
                        byte[] data;
                        using (var m = new MemoryStream())
                        {
                            upsampler.CopyTo(m);
                            data = m.ToArray();
                        }
                        using (var m = new MemoryStream())
                        {
                            // to create a propper WAV header (44 bytes), which begins with RIFF 
                            var w = new WaveFileWriter(m, upsampler.WaveFormat);
                            // append WAV data body
                            w.Write(data,0,data.Length);
                            return m.ToArray();
                        }   
                    }
                }
            }
        }
    }
Sergey Malyutin
  • 1,484
  • 2
  • 20
  • 44
1

It might be added and sorry I can't comment yet due to lack of points. That NAudio ALWAYS writes 46 byte headers which in certain situations can cause crashes. I want to add this in case someone encouters this while searching for a clue why naudio wav files only start crashing certain programs.

I encoutered this problem after figuring out how to convert and/or sample wav with NAudio and was stuck after for 2 days now and only figured it out with a hex editor.

(The 2 extra bytes are located at byte 37 and 38 right before the data subchunck [d,a,t,a,size<4bytes>]. Here is a comparison of two wave file headers left is saved by NAudio 46 bytes; right by Audacity 44 bytes

You can check this back by looking at the NAudio src in WaveFormat.cs at line 310 where instead of 16 bytes for the fmt chunck 18+extra are reserved (+extra because there are some wav files which even contain bigger headers than 46 bytes) but NAudio always seems to write 46 byte headers and never 44 (MS standard). It may also be noted that in fact NAudio is able to read 44 byte headers (line 210 in WaveFormat.cs)

Robin B
  • 1,066
  • 1
  • 14
  • 32