CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 5 of 5
  1. #1
    Join Date
    Mar 2013
    Posts
    2

    Problems writting WAV in C#

    Hi. I am trying to write a program in C# that generate a WAVE (.wav) file by merging more WAVE files. The problem comes when generating the header of the WAV format.

    I am trying to write the header using this: https://ccrma.stanford.edu/courses/4...ts/WaveFormat/

    I must create a 16 bit (bit per sample), 8kHz (samplerate) mono (1 channel) wave file, of unknown time length. Most of the code do the job fine, but I have problems with the ChunkSize and Subchunk2Size bytes. I simply do not understand them. In that document it says it's 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size). I just made it the size of the byte array minus 8 (It says: This is the size of the entire file in bytes minus 8 bytes for the two fields not included in this count). The SubChunk2Size, I just don't understand it. It says it must be NumSamples * NumChannels * BitsPerSample/8, it doesn't work anyway.

    But also, I don't know if each one of these properties must have their bytes inverted. Because, according to this document: http://ist.uwaterloo.ca/~schepers/formats/WAV.TXT, those two properties have a "lo/hi" format. I don't know if this is true or not, since I haven't seen something like this specified in any document. So, I'm pretty much confused of what should I do with these properties. The current code of my program is the following code:

    Code:
       ushort numchannels = 1; // Number of channels
                uint samplerate = 8000; //22050
                uint numsamples = 0; //It becomes numsamples * (uint)DATA.Length lather
                uint bps = 16; //Bits Per Sample
    
    [...]
    
        numsamples = numsamples * (uint)DATA.Length; //DATA is the byte array containing the WAVE
    
                wr.Write(System.Text.Encoding.ASCII.GetBytes("RIFF")); //RIFF
                wr.Write(DATA.Length - 8); //ChunkSize = FileSize - 8?
    
                wr.Write(Encoding.ASCII.GetBytes("WAVE")); //WAVE
                wr.Write(Encoding.ASCII.GetBytes("fmt ")); //fmt
    
                wr.Write((uint)16); //PCM
                wr.Write((short)01); //LINEAR
                wr.Write((short)01); //MONO
    
                wr.Write((uint)samplerate); //Sample rate
    
                wr.Write((uint)(samplerate * numchannels * (bps / 8))); //byte Rate
    
                wr.Write((uint)bps); //Bits Per Sample
    
                wr.Write(Encoding.ASCII.GetBytes("DATA")); //DATA
    
                wr.Write((uint)numsamples * numchannels * (bps / 8)); //SubChunk2Size
    
                wr.Write(DATA, 0, DATA.Length); //Write DATA buffer
    It should work according to those specifications. The problem is... it doesn't work. The audio files generated with this code seems corrupted with any media player. Why? That's what I'd like to know. There is almost no clear information about how to write WAV headers on the internet, and the information I found is quite unclear in most cases, so I come to you with hopes of clarifying things out. Thanks in advance.

  2. #2
    Join Date
    Oct 2006
    Location
    Sweden
    Posts
    3,654

    Re: Problems writting WAV in C#

    One thing that I see is that
    Code:
    uint numsamples = 0; //It becomes numsamples * (uint)DATA.Length lather
    ...
    numsamples = numsamples * (uint)DATA.Length;
    is probably not what you want.

    I suggest that you single step this in the debugger and check that every single entry get the expected value.

    Edit: Welcome to CG by the way and thanks for using code tags in the very first post! Excellent job!
    Last edited by S_M_A; March 18th, 2013 at 04:32 PM.
    Debugging is twice as hard as writing the code in the first place.
    Therefore, if you write the code as cleverly as possible, you are, by
    definition, not smart enough to debug it.
    - Brian W. Kernighan

    To enhance your chance's of getting an answer be sure to read
    http://www.codeguru.com/forum/announ...nouncementid=6
    and http://www.codeguru.com/forum/showthread.php?t=366302 before posting

    Refresh your memory on formatting tags here
    http://www.codeguru.com/forum/misc.php?do=bbcode

    Get your free MS compiler here
    https://visualstudio.microsoft.com/vs

  3. #3
    Join Date
    Mar 2013
    Posts
    2

    Re: Problems writting WAV in C#

    Hi S_M_A.

    Thanks for replying. As you guessed, that line wasn't doing what I wanted it to do. However, I still couldn't get it to work.

    I'm still investigating. However, I made something that also work as I want. What I did is, I just created a .wav file with the characteristics I want with Audacity, of 1 hour of length, and then extracted the header.

    Now, every time I want to write a wav file, I just copy the header, and then copy the rest of the data. I know this is not how it's supposed to be made, but it does the trick, as I am never going to make a file longer than 1 hour. Also, the files produced this way has the right length. Not 1 hour but whatever it needs.

  4. #4
    Join Date
    Oct 2006
    Location
    Sweden
    Posts
    3,654

    Re: Problems writting WAV in C#

    I got curious so I played around a bit with it and this is the result. C# isn't my first language but since I think this could be useful I post it anyway. i haven't tried adding several data blocks into the file but I hope you can make it into something useful anyway.
    Code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IO;
    
    namespace WavMaker
    {
        class Program
        {
            class WavHeader
            {
                public WavHeader(uint subChunk1Size, uint subChunk2Size)
                {
                    chunkId = 0x46464952;   // "RIFF"
                    chunkSize = 4 + (8 + subChunk1Size) + (8 + subChunk2Size);
                    format = 0x45564157;    // "WAVE"
                }
                public void Write(ref BinaryWriter w)
                {
                    w.Write(chunkId);
                    w.Write(chunkSize);
                    w.Write(format);
                }
                uint chunkId;   // BigEndian
                uint chunkSize; // LittleEndian
                uint format;    // BigEndian
            }
            class FmtChunk
            {
                public FmtChunk(ushort numChannels_, uint sampleRate_, ushort bitsPerSample_)
                {
                    subChunk1Id = 0x20746D66;   // "RIFF"
                    subChunk1Size = 16;         // PCM
                    audioFormat = 1;
                    numChannels = numChannels_;
                    sampleRate = sampleRate_;
                    byteRate = numChannels_ * sampleRate_ * bitsPerSample_ / 8;
                    blockAlign = (ushort)(numChannels_ * bitsPerSample_ / 8);
                    bitsPerSample = bitsPerSample_;
                }
                public void Write(ref BinaryWriter w)
                {
                    w.Write(subChunk1Id);
                    w.Write(subChunk1Size);
                    w.Write(audioFormat);
                    w.Write(numChannels);
                    w.Write(sampleRate);
                    w.Write(byteRate);
                    w.Write(blockAlign);
                    w.Write(bitsPerSample);
                }
                public uint Size()
                {
                    return subChunk1Size;
                }
                uint subChunk1Id;       // BigEndian "fmt "
                uint subChunk1Size;     // LittleEndian
                ushort audioFormat;     // LittleEndian
                ushort numChannels;     // LittleEndian
                uint sampleRate;        // LittleEndian
                uint byteRate;          // LittleEndian
                ushort blockAlign;      // LittleEndian
                ushort bitsPerSample;   // LittleEndian
            }
            class DataHeader
            {
                public DataHeader(uint subChunk2Size_)
                {
                    subChunk2Id = 0x61746164;   // "data"
                    subChunk2Size = subChunk2Size_;
                }
                public void Write(ref BinaryWriter w)
                {
                    w.Write(subChunk2Id);
                    w.Write(subChunk2Size);
                }
                uint subChunk2Id;   // BigEndian
                uint subChunk2Size; // LittleEndian
            }
            interface Wave
            {
                void Write(ref BinaryWriter w);
                uint Size();
            }
            class Mono16Tone : Wave
            {
                public Mono16Tone(uint seconds, uint freq, uint sampleRate)
                {
                    uint samples = seconds * sampleRate;
                    wave = new short[samples];
                    for (uint n = 0; n < samples; n++)
                    {
                        wave[n] = (short)(32767 * Math.Sin(freq * 2.0 * Math.PI * n / sampleRate));
                    }
                }
                void Wave.Write(ref BinaryWriter w)
                {
                    foreach (short e in wave) w.Write(e);
                }
                uint Wave.Size()
                {
                    return (uint)(sizeof(short) * wave.Length);
                }
                short[] wave;
            }
            class Mono16DualTones : Wave
            {
                public Mono16DualTones(uint seconds, uint freq1, uint freq2, uint sampleRate)
                {
                    uint samples = seconds * sampleRate;
                    wave = new short[samples];
                    for (uint n = 0; n < samples; n++)
                    {
                        short value1 = (short)(32767 * Math.Sin(freq1 * 2.0 * Math.PI * n / sampleRate));
                        short value2 = (short)(32767 * Math.Sin(freq2 * 2.0 * Math.PI * n / sampleRate));
                        wave[n] = (short)((value1 + value2) / 2);
                    }
                }
                void Wave.Write(ref BinaryWriter w)
                {
                    foreach (short e in wave) w.Write(e);
                }
                uint Wave.Size()
                {
                    return (uint)(sizeof(short) * wave.Length);
                }
                short[] wave;
            }
            class Stereo16Tone : Wave
            {
                public Stereo16Tone(uint seconds, uint freq, uint sampleRate)
                {
                    uint samples = seconds * sampleRate;
                    wave = new short[2 * samples];
                    for (uint n = 0; n < samples; n++)
                    {
                        short value = (short)(32767 * Math.Sin(freq * 2.0 * Math.PI * n / sampleRate));
                        wave[2 * n] = value;
                        wave[2 * n + 1] = value;
                    }
                }
                void Wave.Write(ref BinaryWriter w)
                {
                    foreach (short e in wave) w.Write(e);
                }
                uint Wave.Size()
                {
                    return (uint)(sizeof(short) * wave.Length);
                }
                short[] wave;
            }
            class Stereo16DualTones : Wave
            {
                public Stereo16DualTones(uint seconds, uint freq1, uint freq2, uint sampleRate)
                {
                    uint samples = seconds * sampleRate;
                    wave = new short[2 * samples];
                    for (uint n = 0; n < samples; n++)
                    {
                        short value = (short)(32767 * Math.Sin(freq1 * 2.0 * Math.PI * n / sampleRate));
                        wave[2 * n] = value;
                        value = (short)(32767 * Math.Sin(freq2 * 2.0 * Math.PI * n / sampleRate));
                        wave[2 * n + 1] = value;
                    }
                }
                void Wave.Write(ref BinaryWriter w)
                {
                    foreach (short e in wave) w.Write(e);
                }
                uint Wave.Size()
                {
                    return (uint)(sizeof(short) * wave.Length);
                }
                short[] wave;
            }
            static void Main(string[] args)
            {
                uint seconds = 10;
                uint freq = 440;
                uint sampleRate = 8000;
                ushort bitsPerSample = 16;
                ushort numChannels = 1;
                ushort tones = 2;
    
                String fname = "";
                Wave wave = null;
                switch (numChannels)
                {
                    case 1:
                        switch (tones)
                        {
                            case 1:
                                fname = "mono1.wav";
                                wave = new Mono16Tone(seconds, freq, sampleRate);
                                break;
                            case 2:
                                fname = "mono2.wav";
                                wave = new Mono16DualTones(seconds, freq, 2 * freq, sampleRate);
                                break;
                        }
                        break;
                    case 2:
                        switch (tones)
                        {
                            case 1:
                                fname = "stereo1.wav";
                                wave = new Stereo16Tone(seconds, freq, sampleRate);
                                break;
                            case 2:
                                fname = "stereo2.wav";
                                wave = new Stereo16DualTones(seconds, freq, 2 * freq, sampleRate);
                                break;
                        }
                        break;
                }
    
                DataHeader dataHeader = new DataHeader(wave.Size());
                FmtChunk fmtChunk = new FmtChunk(numChannels, sampleRate, bitsPerSample);
                WavHeader wavHeader = new WavHeader(fmtChunk.Size(), wave.Size());
    
                FileStream f = new FileStream(fname, FileMode.Create, FileAccess.Write);
                BinaryWriter w = new BinaryWriter(f);
                wavHeader.Write(ref w);
                fmtChunk.Write(ref w);
                dataHeader.Write(ref w);
                wave.Write(ref w);
                f.Close();
            }
        }
    }
    Last edited by S_M_A; March 21st, 2013 at 03:24 PM.
    Debugging is twice as hard as writing the code in the first place.
    Therefore, if you write the code as cleverly as possible, you are, by
    definition, not smart enough to debug it.
    - Brian W. Kernighan

    To enhance your chance's of getting an answer be sure to read
    http://www.codeguru.com/forum/announ...nouncementid=6
    and http://www.codeguru.com/forum/showthread.php?t=366302 before posting

    Refresh your memory on formatting tags here
    http://www.codeguru.com/forum/misc.php?do=bbcode

    Get your free MS compiler here
    https://visualstudio.microsoft.com/vs

  5. #5
    Join Date
    Sep 2014
    Posts
    1

    Re: Problems writting WAV in C#

    S_M_A
    Many thanks for posting WavMaker. Beautiful C# code - way beyond my limits. I am using it to create a chirp for my Arduino Due, which will output wav files. Here is the main part of the butchery of your code:

    public Mono16DualTones(uint seconds, uint freq1, uint freq2, uint sampleRate)
    {
    float duration = 0.053333F; //affects but does not equal length of pulse, why?
    uint samples = 2048; //lines of data
    wave = new short[samples];
    float newB = 0;
    for (uint n = 1; n < samples; n++)
    {
    newB = newB + duration / samples; // This code adds one time unit to newB with each line of data
    short value1 = (short)(5000 * 2 * Math.PI * Math.Cos((freq2 - freq1) * newB * newB / (2 * duration) + freq1 * newB + 1.55));
    wave[n] = (short)value1;
    }
    }

    The algorithm is taken from a paper by D W Caress in Monterey, sort of...

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured