Monday, 8 November 2010

Merging MP3 Files with NAudio in C# and IronPython

If you would like to concatenate MP3 files using NAudio, it is quite simple to do. I recommend getting the very latest source code and building your own copy of NAudio, as this will work best with some of the changes that are in preparation for NAudio 1.4.

Here’s the C# code for a function that takes MP3 filenames, and writes a combined MP3 to the output stream:

public static void Combine(string[] inputFiles, Stream output)
{
    foreach (string file in inputFiles)
    {
        Mp3FileReader reader = new Mp3FileReader(file);
        if ((output.Position == 0) && (reader.Id3v2Tag != null))
        {
            output.Write(reader.Id3v2Tag.RawData, 0, reader.Id3v2Tag.RawData.Length);
        }
        Mp3Frame frame;
        while ((frame = reader.ReadNextFrame()) != null)
        {
            output.Write(frame.RawData, 0, frame.RawData.Length);
        }
    }
}

And here’s an IronPython script (just put NAudio.dll in the same folder as the mp3merge.py script):

import clr
clr.AddReference('NAudio.dll')

import sys
from NAudio.Wave import Mp3FileReader
from System.IO import File

def GetAllFrames(reader):
    while True:
        frame = reader.ReadNextFrame()
        if frame:
            yield frame
        else:
            return

def Merge(files, outputStream):
    for file in files:
        with Mp3FileReader(file) as reader:
            if reader.XingHeader:
                print 'discarding a Xing header'
            if not outputStream.Position and reader.Id3v2Tag:
                outputStream.Write(reader.Id3v2Tag.RawData, 0, reader.Id3v2Tag.RawData.Length)                
            for frame in GetAllFrames(reader):
                outputStream.Write(frame.RawData, 0, frame.RawData.Length);
            
if __name__ == '__main__':
    if len(sys.argv) < 3:
        print "Usage: ipy mp3merge.py output.mp3 File1.mp3 File2.mp3"
    else:
        with File.OpenWrite(sys.argv[1]) as outStream:
            Merge(sys.argv[2:],outStream)

Notes:

I simply copy across the ID3v2 tag from the first MP3 file if present. All other ID3v2 tags are discarded (as are ID3v1 tags). Also, I discard the Xing frame from VBR files. It could easily be re-included if desired, although it’s information will not necessarily be valid about the combined MP3 file. One final thing, I wouldn’t recommend merging MP3 files of different sample rates, or mixing mono with stereo, as it could cause various players issues.

6 comments:

KOS_MOS said...

I install Python for .Net, put NAudio.dll to script directory, but when i run script - i have error:

c:\Temp\pmerge>C:\Python.Net\python.exe mp3merge.py
Traceback (most recent call last):
File "mp3merge.py", line 2, in
clr.AddReference('NAudio.dll')
System.IO.FileNotFoundException

Please help!

KOS_MOS said...

I'we installed Python for .Net, put NAudio.dll to script directory, but when i run script - i have error:

c:\Temp\pmerge>C:\Python.Net\python.exe mp3merge.py
Traceback (most recent call last):
File "mp3merge.py", line 2, in
clr.AddReference('NAudio.dll')
System.IO.FileNotFoundException

Please help me!

Unknown said...

I always run IronPython with ipy.exe. Not sure if that is your issue though.

Anonymous said...

I want to merger multiple mp3 files- say 3 files. So i've array of 3 mp3 files path. Secong one of these files is Blank file of 20 sec. when i merge these blank file doesnt merge. Any suggestion?

Anonymous said...

Using the above code I have merged around 80 files. I have also created an audio marker using following code.

oStringBuilder.AppendFormat("",
Path.GetFileNameWithoutExtension(file), Math.Round(oCurrentTime,3), Math.Round(oCurrentTime + reader.TotalTime.TotalSeconds,3));

And then I use these markers to play specific section of the merged MP3 in various browsers.

The audio markers work alright in chrome but in Safari and IE the markers are inaccurate, any idea?

Anonymous said...

Using the above code I have merged around 80 files. I have also created an audio marker using following code.

oStringBuilder.AppendFormat("",
Path.GetFileNameWithoutExtension(file), Math.Round(oCurrentTime,3), Math.Round(oCurrentTime + reader.TotalTime.TotalSeconds,3));

And then I use these markers to play specific section of the merged MP3 in various browsers.

The audio markers work alright in chrome but in Safari and IE the markers are inaccurate, any idea?