• We see that you're not registered. Please read this thread and if you want, sign up on the forum.

Tutorial [C#] How to Merge Audio Files with NAudio

Shenandoah

Access Write Violation
Admin
Legend
Joined
Nov 1, 2019
Posts
90
Points
18
Reaction score
48
Quality Posts
1
Hello guys, welcome to my quick tutorial on how to use the NAudio library to merge multiple audio files together. This will just briefly cover the amazing things you can do with the library. Let's get started!

Table of Contents
1. Setting up NAudio in Visual Studio
2. Basics
3. Sample Providers
4. Merging audio files with MixingSampleProvider
5. Creating an MP3 file containing the mixed audio files


Setting up NAudio in Visual Studio
First things first. We need to actually set up the NAudio library in our project, so we can use its files. We'll be installing the NAudio and the Lame extension of the NAudio library via the NuGet package manager.

Start by creating a new project in Visual Studio. After the project is created, right click your project in Solution Explorer and click Manage NuGet Packages...

1.png


You'll be greeted with a window where you can type in the name of any package, and then you can proceed to download it. Let's type in NAudio, and download the two packages we want.

2.png


Install the NAudio and NAudio.Lame packages. The Lame package will be used for creating MP3 files that we'll show later in this tutorial.

After the packages have been installed, you can go ahead and start using them in the program. Let's start with the basics.


Basics
To manipulate audio files, we first need to load them into memory! We can do so by using the AudioFileReader class.

C#:
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Tutorial
{
    class Program
    {
        public Program()
        {
            string filePath = "C:\\Users\\Public\\Music\\Sample Music\\Kalimba.mp3";
            var file = new AudioFileReader(filePath);
        }

        static void Main(string[] args)
        {
            new Program();
        }
    }
}
Interesting. We now have a AudioFileReader object that we can use to manipulate that audio file. For instance, we could set the volume of it like this: file.Volume = 0.5f;

Alright. Let's write out a WAV file and see if it works.
C#:
var waveProvider = file.ToWaveProvider();
WaveFileWriter.CreateWaveFile("kalimbu.wav", waveProvider);
And it should work if you did everything properly! I'll show you how to write MP3 files later in the tutorial.


Sample Providers
To manipulate audio files even further, we'll use something called a sample provider. These are concrete classes that implement the ISampleProvider interface, and depending on what you want to do with the audio, you'll use different sample provider implementations. Since we want to merge two or more audio files together, we'll be using a class called the MixingSampleProvider.

Merging audio files with MixingSampleProvider
First we create an instance of it, and we pass in the audio file we loaded earlier.

var mixer = new MixingSampleProvider(new[] { file });


If we loaded more than one audio file prior to the declaration of that mixer variable, we could pass in all of the audio files into the array we pass into the constructor. Alright, let's mix two songs together! It's very straightforward.

C#:
string filePath2 = "C:\\Users\\Public\\Music\\Sample Music\\Sleep Away.mp3";
var file2 = new AudioFileReader(filePath2);
We first get the second audio file we want to merge with the first one. We then create the instance of the MixingSampleProvider, and pass in both audio file readers into it: var mixer = new MixingSampleProvider(new[] { file, file2 });

And the last thing we need to do is change the waveProvider variable and make it call the mixer's toWaveProvider method instead. var waveProvider = mixer.ToWaveProvider();

Alright, run the program and see if it works! The audio file should be written out in your project's debug folder, if you're in debug mode (which you most likely are). Alright, let's move on.


Creating an MP3 file containing the mixed audio files
So far, we've only created WAV files, but you might want to create MP3 files, sometimes. That's where the Lame (yes, lame) extension of the NAudio library comes in. We'll use the LameMP3FileWriter class to create the MP3 file. However, this class will the data of our mixer sample provider, but we first need to "convert" the mixer sample provider into a wave stream, because we need the stream of audio data, so we can copy that data over to the LameMP3FileWriter instance, so it can actually output an MP3 file containing sounds, and not just empty silence. But, before we get to that, let's create our instance of the LameMP3FileWriter class first.

var mp3Writer = new LameMP3FileWriter("mp3kalimbu", mixer.WaveFormat, 128);

The next thing we need to do is copy over the data from our mixer sample provider over to our mp3Writer instance. But, since there are no methods for converting a wave provider into a wave stream, we have to do this ourselves, unfortunately. However, I found a class online that did this for us, so we can just use that class.

Alright, create a new class and call it WaveProviderToWaveStream, and paste this code in:

C#:
using NAudio.Wave;
using System;

public class WaveProviderToWaveStream : WaveStream
{
    private readonly IWaveProvider source;
    private long position;

    public WaveProviderToWaveStream(IWaveProvider source)
    {
        this.source = source;
    }

    public override WaveFormat WaveFormat
    {
        get { return source.WaveFormat; }
    }

    /// <summary>
    /// Don't know the real length of the source, just return a big number
    /// </summary>
    public override long Length
    {
        get { return Int32.MaxValue; }
    }

    public override long Position
    {
        get
        {
            // we'll just return the number of bytes read so far
            return position;
        }
        set
        {
            // can't set position on the source
            // n.b. could alternatively ignore this
            throw new NotImplementedException();
        }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        int read = source.Read(buffer, offset, count);
        position += read;
        return read;
    }
}
Alright, we can now use this class in our main class. WaveProviderToWaveStream waveStream = new WaveProviderToWaveStream(waveProvider);

All we have to do now is copy over the data and then dispose of the mp3Writer and waveStream objects.

C#:
waveStream.CopyTo(mp3Writer);
waveStream.Dispose();
mp3Writer.Dispose();
If you run the program, it should work now. You'll see a newly created MP3 file in your project's debug directory. :)

Thank you for reading, and I hope this tutorial helped you somewhat. If you have any questions about NAudio, just leave a reply and I'll get back to you. If it's something complex, I might have to write a tutorial on it, but if it's something simple, I'll just explain things in a reply instead. Have a nice day!

Here's the entire source code of the program we just wrote:


C#:
using NAudio.Lame;
using NAudio.Wave;
using NAudio.Wave.SampleProviders;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Tutorial
{
    class Program
    {
        public Program()
        {
            string filePath = "C:\\Users\\Public\\Music\\Sample Music\\Kalimba.mp3";
            var file = new AudioFileReader(filePath);
            string filePath2 = "C:\\Users\\Public\\Music\\Sample Music\\Sleep Away.mp3";
            var file2 = new AudioFileReader(filePath2);

            var mixer = new MixingSampleProvider(new[] { file, file2 });

            var waveProvider = mixer.ToWaveProvider();

            var mp3Writer = new LameMP3FileWriter("mp3kalimbu.mp3", mixer.WaveFormat, 128);
            WaveProviderToWaveStream waveStream = new WaveProviderToWaveStream(waveProvider);
            waveStream.CopyTo(mp3Writer);
            waveStream.Dispose();
            mp3Writer.Dispose();
        }

        static void Main(string[] args)
        {
            new Program();
        }
    }
}
 

Space Pirate

Working on it!
Admin
Legend
Joined
Nov 1, 2019
Location
Home
Posts
57
Points
19
Reaction score
43
Quality Posts
2
There is surprisingly little information about how to do basic stuff in nAudio and it's a pain to work with.

Also you should use const string and no idea why you're using
System.Threading.Tasks.


Good work anyhoe.
 

Shenandoah

Access Write Violation
Admin
Legend
Joined
Nov 1, 2019
Posts
90
Points
18
Reaction score
48
Quality Posts
1
There is surprisingly little information about how to do basic stuff in nAudio and it's a pain to work with.

Also you should use const string and no idea why you're using
System.Threading.Tasks.


Good work anyhoe.
Have you even bothered looking? The github repository is literally filled with examples on how to do various things.

Thanks for the feedback. However, it's not constructive feedback unless you provide reasons as to why I should do as you say. Why should I use const string? For performance-related reasons? If so, that's redundant. The bulk of the time spent in this application will be in actually creating the new audio files after they've been merged together. Spending time focusing on micro-optimizations prematurely is a bad habit, especially in an example program where the entire point of the code is to demonstrate how things work.

The System.Threading.Tasks import was added in there automatically when I created the project, I simply forgot to remove it. My bad.

And, thanks. <3
 

Space Pirate

Working on it!
Admin
Legend
Joined
Nov 1, 2019
Location
Home
Posts
57
Points
19
Reaction score
43
Quality Posts
2
Have you even bothered looking? The github repository is literally filled with examples on how to do various things.

Thanks for the feedback. However, it's not constructive feedback unless you provide reasons as to why I should do as you say. Why should I use const string? For performance-related reasons? If so, that's redundant. The bulk of the time spent in this application will be in actually creating the new audio files after they've been merged together. Spending time focusing on micro-optimizations prematurely is a bad habit, especially in an example program where the entire point of the code is to demonstrate how things work.

The System.Threading.Tasks import was added in there automatically when I created the project, I simply forgot to remove it. My bad.

And, thanks. <3
Sorry was on the toilet at work so i didn't have enough time.
You use const should be used for variables that does not change during the lifetime of the application. There is a small performance gain by using const but it is negligible in this scenario as it is a tutorial.

As for the github repository, yes trust me I've been there viewed every issue a few months ago and browsed the wiki multiple times. It's up to my standards.
 

Shenandoah

Access Write Violation
Admin
Legend
Joined
Nov 1, 2019
Posts
90
Points
18
Reaction score
48
Quality Posts
1
Sorry was on the toilet at work so i didn't have enough time.
You use const should be used for variables that does not change during the lifetime of the application. There is a small performance gain by using const but it is negligible in this scenario as it is a tutorial.
Strings are immutable so they can't change, and the variables declared in the tutorial are used immediately, so I don't see how they can be changed before being used. I'd argue that it's a bit of an "overkill" to use the const word for everything by default as long as you don't want it to change, but I appreciate the thought.
 

Space Pirate

Working on it!
Admin
Legend
Joined
Nov 1, 2019
Location
Home
Posts
57
Points
19
Reaction score
43
Quality Posts
2
Strings are immutable so they can't change, and the variables declared in the tutorial are used immediately, so I don't see how they can be changed before being used. I'd argue that it's a bit of an "overkill" to use the const word for everything by default as long as you don't want it to change, but I appreciate the thought.
Lmao they are within the function. Nvm thought they we're defined in the class for some reason. Don't mind me.
 

Shili

New member
Joined
Dec 13, 2021
Posts
1
Points
1
Reaction score
0
Quality Posts
Thanks a lot for your great tutorial "Shenandoah".
Unfortunately, I got stuck in the first step of NAudio package installation.
Here is my error:
"Could not install package 'NAudio.Core 2.0.0'. You are trying to install this package into a project that targets '.NETFramework,Version=v4.0', but the package does not contain any assembly references or content files that are compatible with that framework..."
Do you know what is the proper version of .NetFramework for this package, or other any solution to fix it?
Thank you in advance.
 

Space Pirate

Working on it!
Admin
Legend
Joined
Nov 1, 2019
Location
Home
Posts
57
Points
19
Reaction score
43
Quality Posts
2
Do you know what framework your project is targeting?
You could also try and find a newer nuget package by searching for NAudio in the nuget package manager.

Thanks a lot for your great tutorial "Shenandoah".
Unfortunately, I got stuck in the first step of NAudio package installation.
Here is my error:
"Could not install package 'NAudio.Core 2.0.0'. You are trying to install this package into a project that targets '.NETFramework,Version=v4.0', but the package does not contain any assembly references or content files that are compatible with that framework..."
Do you know what is the proper version of .NetFramework for this package, or other any solution to fix it?
Thank you in advance.
 

Attachments

JasonGreb

New member
Joined
Jan 17, 2022
Posts
1
Points
3
Reaction score
1
Quality Posts
Hi, I was wondering if you know the best way to add some sort of delay between the clips? Thanks for the awesome tutorial, definitely helped out a bunch!
 

Shenandoah

Access Write Violation
Admin
Legend
Joined
Nov 1, 2019
Posts
90
Points
18
Reaction score
48
Quality Posts
1
Hi, I was wondering if you know the best way to add some sort of delay between the clips? Thanks for the awesome tutorial, definitely helped out a bunch!
Thanks. :)

Yeah, I did something similar when writing an automatic watermark adder. I don't have the source anymore unfortunately, but have a look at
OffsetSampleProvider (link). That should be the answer to your question.
 
Top