---
title: "Join audio files in C#"
url: https://recursiveai.org/oneaudio/cookbook/join-audio/
description: "Concatenate WAV, MP3, FLAC or Ogg files into one WAV in C#, converting each to the same sample rate and channels, with a gap between."
version: "1.4.0"
---

# Join audio files in C#

Load each file at one sample rate and channel count with `LoadOptions`, then write them one after another into a single `WavEncoder`. Files of different formats and rates join cleanly because each is converted as it loads. The inputs are short synthetic test files from the oneaudio repository, an MP3, a FLAC and an Opus file at three different rates.

```shell
dotnet new console -o JoinAudio && cd JoinAudio
dotnet add package RecursiveAI.OneAudio --version 1.4.0
t=https://raw.githubusercontent.com/recursive-ai-oss/oneaudio-csharp/v1.4.0/testdata
curl -L -o one.mp3 $t/mp3/small_44100_2_joint.mp3
curl -L -o two.flac $t/flac/stereo16_seektable.flac
curl -L -o three.opus $t/opus/celt-stereo.opus
```

Put this in `Program.cs` and run `dotnet run`:

```csharp
using OneAudio;

string[] inputs = ["one.mp3", "two.flac", "three.opus"];
var common = new LoadOptions { SampleRate = 44100, Channels = 2 };
float[] gap = new float[44100 / 4 * 2];         // a quarter second of silence

var format = new AudioFormat(44100, 2);
var pcm16 = new WavEncoderSettings { SampleType = NativeSampleType.PcmSigned16 };
using (var wav = new WavEncoder(File.Create("joined.wav"), format, pcm16,
                                leaveOpen: false))
{
    for (int i = 0; i < inputs.Length; i++)
    {
        int rate;
        using (IAudioDecoder d = Audio.Open(inputs[i]))
            rate = d.Format.SampleRate;
        AudioClip clip = Audio.Load(inputs[i], common);
        Console.WriteLine($"{inputs[i],-11}{rate,6} Hz -> " +
                          $"{clip.Frames,6} frames at 44.1 kHz");
        wav.Write(clip.Samples.Span);
        if (i < inputs.Length - 1)
            wav.Write(gap);
    }
}

using IAudioDecoder joined = Audio.Open("joined.wav");
Console.WriteLine($"joined.wav: {joined.LengthFrames} frames, " +
                  $"{joined.LengthFrames / 44100.0:F3} s");
```

```text
one.mp3     44100 Hz ->  26460 frames at 44.1 kHz
two.flac    22050 Hz -> 132000 frames at 44.1 kHz
three.opus  48000 Hz ->  44100 frames at 44.1 kHz
joined.wav: 224610 frames, 5.093 s
```

## Notes

- Pick the rate and channels of the output once; `LoadOptions` resamples (polyphase filter) and up- or down-mixes each input to match. Mono becomes stereo as a copy.
- `Audio.Load` holds each input in memory, which is fine for clips and tracks. For hours of audio, read each input with `Audio.Open` and `Read` in blocks, and resample with `AudioResampler` (see [Resample audio](https://recursiveai.org/oneaudio/cookbook/resample-audio/)).
- Opus files, M4A files and MP3 files with a LAME/Xing header record their encoder padding, and oneaudio trims it, so they add no extra silence at the joins. Raw AAC and header-less MP3 keep theirs.
- For a crossfade instead of a gap, or to layer files on top of each other, use the mixer: see [Mix audio files](https://recursiveai.org/oneaudio/cookbook/mix-audio/).

## Limits

- The output is WAV here; convert it to FLAC or Opus with `Audio.Transcode` afterwards. oneaudio doesn’t write MP3.
- Two files at different loudness stay different. To even them out, normalize each one first: see [Normalize loudness](https://recursiveai.org/oneaudio/cookbook/normalize-loudness/).
