20

I need the ability to join 2 or more .wav files together in to one .wav file. I must do this programmatically, using C# (3rd-party products are not an option). I know of the System.Media.SoundPlayer class, but I am not looking to play the the .wav, but only to create it.

Cœur
  • 37,241
  • 25
  • 195
  • 267
gangelo
  • 3,034
  • 4
  • 29
  • 43

5 Answers5

32

Here's a basic WAV concatenation function built using NAudio. This will ensure that only the data chunks are concatenated (unlike the code example in this CodeProject article linked in another answer). It will also protect you against concatenating WAV files that do not share the same format.

public static void Concatenate(string outputFile, IEnumerable<string> sourceFiles)
{
    byte[] buffer = new byte[1024];
    WaveFileWriter waveFileWriter = null;

    try
    {
        foreach (string sourceFile in sourceFiles)
        {
            using (WaveFileReader reader = new WaveFileReader(sourceFile))
            {
                if (waveFileWriter == null)
                {
                    // first time in create new Writer
                    waveFileWriter = new WaveFileWriter(outputFile, reader.WaveFormat);
                }
                else
                {
                    if (!reader.WaveFormat.Equals(waveFileWriter.WaveFormat))
                    {
                        throw new InvalidOperationException("Can't concatenate WAV Files that don't share the same format");
                    }
                }

                int read;
                while ((read = reader.Read(buffer, 0, buffer.Length)) > 0)
                {
                    waveFileWriter.WriteData(buffer, 0, read);
                }
            }
        }
    }
    finally
    {
        if (waveFileWriter != null)
        {
            waveFileWriter.Dispose();
        }
    }

}
Mark Heath
  • 48,273
  • 29
  • 137
  • 194
  • good sample... I confirm that the wavformat comparison does not work as expected, as noted by davidair. – Peter Walke Jan 22 '12 at 22:33
  • after adding the nuget Naudio into project and using above funtion to merge thowing one build error. "Failed to resolve "System.Runtime.InteropServices.StandardOleMarshalObject" reference from "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Why is this happening? – Riyas Apr 23 '18 at 06:17
  • Why not add this into the library?? – frenchie Feb 19 '20 at 14:45
1

Use from How to join .Wav files together

    private void JoinWav()
    {
        string[] files = new string[] { "1990764-ENG-CONSEC-RESPONSE7.WAV","1990764-ND_A.WAV", "1990764-SIGHT-SP.WAV",
            "1990764-SP-CONSEC-RESPONSE6.WAV","1990764-VOCABWORD-004-12-SP.WAV","bi-consec-1-successful.wav",
            "bi-transition-instruct.wav","nd_B.wav","sightreceived_B.wav","teststamp_A.wav" };
        AudioCompressionManager.Join("res.wav", files);
    }
Aleks
  • 492
  • 5
  • 5
1

If you need get only byte array, to insert in database or somebody else. You may use memory stream:

        public static byte[] Concatenate(IEnumerable<byte[]> sourceData)
    {
        var buffer = new byte[1024 * 4];
        WaveFileWriter waveFileWriter = null;

        using (var output = new MemoryStream())
        {
            try
            {
                foreach (var binaryData in sourceData)
                {
                    using (var audioStream = new MemoryStream(binaryData))
                    {
                        using (WaveFileReader reader = new WaveFileReader(audioStream))
                        {
                            if (waveFileWriter == null)
                                waveFileWriter = new WaveFileWriter(output, reader.WaveFormat);
                            else
                                AssertWaveFormat(reader, waveFileWriter);

                            WaveStreamWrite(reader, waveFileWriter, buffer);
                        }
                    }
                }

                waveFileWriter.Flush();

                return output.ToArray();
            }
            finally
            {
                waveFileWriter?.Dispose();
            }
        }
    }

    private static void AssertWaveFormat(WaveFileReader reader, WaveFileWriter writer)
    {
        if (!reader.WaveFormat.Equals(writer.WaveFormat))
        {
            throw new InvalidOperationException("Can't concatenate WAV Files that don't share the same format");
        }
    }

    private static void WaveStreamWrite(WaveFileReader reader, WaveFileWriter writer, byte[] buffer)
    {
        int read;
        while ((read = reader.Read(buffer, 0, buffer.Length)) > 0)
        {
            writer.Write(buffer, 0, read);
        }
    }
1

Check out this codeproject example, seems to be exactly what you need with a good explanation of how to do it too:

Concatenating Wave Files Using C# 2005

It seems to comprise essentially of extracting and merging the sound data from all the wav files into one chunk of data with a new file header on top

EDIT: I have no experience of using this, nor am I an expert. I just came across this article and thought it may be useful. See Mark Heath's answer for a better solution

Iain Ward
  • 9,850
  • 5
  • 34
  • 41
  • I would advise against using the code from this article. It assumes that the fmt chunk is always exactly the same length, and that the data chunk appears in exactly the same place in all WAV files. It assumes that no other chunks are present. None of these can be taken for granted with WAV files, and so it could easily create a garbage WAV file. – Mark Heath Jul 21 '11 at 15:33
  • @MarkHeath exactly same problem occurs to me, it create a merged but unsupported wav file, but its works for android not with ios. Can anyone having alternate ideas please suggest. Thanks in advance. – Riyas Apr 23 '18 at 06:23
1

One comment on Mark's answer:

The == operator does not seem to work for me when comparing wave formats. It's safer to do this:

if (!reader.WaveFormat.Equals(waveFileWriter.WaveFormat))

Alternatively, you could wrap the reader in a WaveFormatConversionStream and get rid of the format check altogether (not sure if it will work on all scenarios but I was able to succesfully test it).

David Airapetyan
  • 5,301
  • 4
  • 40
  • 62