lyncd

How to losslessly concatenate / merge MP3 files

You’d think it would be very easy to combine multiple MP3 files into one, while preserving all the ID3 tag metadata and without re-encoding the audio (which is lossy). Well, it actually isn’t that hard at all, but since Google couldn’t find the answer for me, I thought I’d write it up.

Update 3/2011: Although it’s possible you’ll come back to the method on this page, you should check out my new-and-improved approach.

I did this on my Powerbook, so these directions should work fine on any Mac/Linux/BSD/Unix system.

There is a handy command-line utility for combining MP3 files, mp3wrap. Unfortunately, it doesn’t correct the audio duration tags/headers (so iTunes/iPod will only see the first part of the file), and it clobbers your ID3 tags with its own (so that the file can later be unmerged with mp3split — this is a “feature”). There’s also the easy cat solution (cat 1.mp3 2.mp3 > all.mp3), but it doesn’t correct the duration, either. Maybe you don’t care about ID3 tags, and your playback software doesn’t care about a wrong duration header (some ignore it) — in that case, go away! But if you want to get your MP3 files right or you’re using something like iTunes, which does care, read on.

Instructions

Install mp3wrap, ffmpeg and id3lib if you don’t have them already. On Linux, you’ll likely just use your distro’s package manager to do this in a few seconds. On my Mac, I just installed mp3wrap and id3lib from source (easy “./configure && make && make install”) and used the ffmpeg binary from ffmpegX (since Fink mangled dependencies).

Do this (combines 1.mp3, 2.mp3 and 3.mp3 into all.mp3, and copies ID3 tags from 1.mp3):

mp3wrap tmp.mp3 1.mp3 2.mp3 3.mp3
ffmpeg -i tmp_MP3WRAP.mp3 -acodec copy all.mp3 && rm tmp_MP3WRAP.mp3
id3cp 1.mp3 all.mp3

Here’s what’s happening:

  1. mp3wrap merges the files. Unfortunately, it also leaves wrong file duration headers and clobbers any original ID3 tags.
  2. ffmpeg deletes the incorrect file duration. You could also use something else, like mplayer/mencoder (-oac copy option) here.
  3. id3cp copies the ID3 tags from 1.mp3 to all.mp3

Filed under: Systems.  Tagged: , .

22 comments »

  • I have an error while trying to convert wraped mp3 with ffmpeg:

    error, non monotone timestamps 2351 >= 0
    av_interleaved_write_frame(): Error while opening file

    It happends only with ‘copy’ codec, not with fair libmp3lame
    Could you show exactly command for mplayer? It doesn’t want to encode from mp3. The error is : “Audio file file format detected.Video stream is mandatory!”

    • You definitely want to use the copy “codec” and not lame, because the whole point is not just losslessly append the files, not decode/reencode, which means quality loss and more of that lovely “listening underwater” mp3 ickiness.

      Not sure why ffmpeg didn’t work for you … could be your audio files or your ffmpeg build. The error messages suggests that your files are screwy — which would be a totally different problem. You might try experimenting with different files and/or different build on a different machine. Make sure you’ve built ffmpeg with mp3 support, test it on a single, known-clean file etc.

      I don’t have an mplayer install handy … my guess is that you’d also have to use the -of lavf and -lavcopts [...] to use libavcodec to use the mp3 container format.

      This step is really where you can swap in whatever program you want. For instance I have a similar workflow where I use mp3gain here to normalize the file (so if you don’t mind also normalizing the file, mp3gain -s rc filename.mp3 will also work for step 2. You probably don’t even really need to fix the duration header, just delete it. I only used ffmpeg/libavcodec here because it was the first thing I thought of and it did the trick so I didn’t have to write any code.

      And as a reminder be sure you’ve tried the easy cat 1.mp3 2.mp3 > all.mp3 way!

      • You probably don’t even really need to fix the duration header, just delete it.

        But how can I delete header? I think it is all I really need, but I cant find the way to do it.

        • Well, I don’t know what the “bad” header is in your case that mp3wrap is leaving behind — ID3 v1 or v2 (ID3 v1 is appended at the end of the file so technically not a “header”), or it could be Ape or Vorbis or some other metadata. To be 100% sure you could just take everything out, so that all you have are MP3 frames in a file.

          There seem to be some programs out there (i.e. TidyMP3 and Tag that will do this for you — i.e. they have a mode where they don’t try to identify tags, they just look for MP3 frames and strip everything else. I’m not testing/recommending and don’t want to go any deeper into this, just hoping to point you in the right direction with what I found using the Google. Potentially any tag editor that has the ability to delete things (i.e. the 12 duration bytes in “enhanced” ID3 v1 and/or the TLEN section in ID3 v2) might work, depending on what the issue is with your files.

          Personally, I would just look in my actual files (i.e. hex editor) and identify the offending bits, if they’re all from the same place. But I really don’t want to go into that!

  • I’ve found the problem case. In my current ffmpeg version (say thx to svn builds) there is a bug with file duration while acodec = copy. It is discussed in authors mailing list. So, I think it will be fixed in next release included into repository (yes, I am Ubuntu user). I’ll try to delete duration information at all as you’ve recomended.

  • Found a simple replacement for ffmpeg, since it appears to have a bug in the Ubuntu version.

    A little program called vbrfix seemed to do the trick for me.

    Lame had been used for the encoding of the original files, before using mp3wrap to combine them.

    mp3wrap tmp.mp3 1.mp3 2.mp3 3.mp3
    vbrfix -ri1 -ri2 -lameinfo tmp_MP3WRAP.mp3 all.mp3 && rm tmp_MP3WRAP.mp3
    id3cp 1.mp3 all.mp3

  • Great – thanks for the tips – one of those things that you think should be simple but ends up quite complicated (don’t get me started on changing video framerates).

    Only thing I couldn’t quite work is what we gain by using mp3wrap vs just cat – since both spit out incorrect headers that we have to fix with ffmpeg, why not use the simpler option?

  • Thanks, very useful information. On ubuntu Jaunty, the following packages are required: ffmpeg libid3-3.8.3-dev

  • Thanks for the info. But It seems mp3wrap has got some limit on either the number of input files or total size.
    do anyone know of this limitation, is there any way around?

  • There is another way, without going through mp3wrap.
    cat file list... | ffmpeg -f mp3 -i - -acodec copy outfile.mp3
    And then the id3 copy command.

    • The reason I didn’t just use cat was that, if you have ID3 tags on the beginning (v2) and/or end (v1) of your files, they will all get concatenated together amongst the mp3 frames.

      This should not be noticeable in the audio playback (most players will just skip over the invalid data), and the tags don’t take up much space, so you can do it this way if you want.

      But, if you’re just going to cat the files, you don’t even need the other steps! Just do “cat 1.mp3 2.mp3 > all.mp3” like I wrote in the post, and you’re done. (As long as you have an ID3v2 tag at the beginning of file 1, it’ll still be read.)

      I wanted to strip these out and end with a “clean” file in the end, which is why I went looking for mp3wrap in the first place.

      The other problem with using cat is that the file duration will still be screwed up in iTunes.

  • Thanks for the info. But It seems mp3wrap has got some limit on either the number of input files or total size.

    If you have wild-carded the mp3 input file list, you just be running into the shell expansion limit. If so, use the xargs trick:

    $ find -name \*mp3|xargs mp3wrap all.mp3

  • Just thought you may want to know you can substitute the first line of code for

    mp3wrap tmp.mp3 *.mp3

    to wrap all files in the folder together

  • “There is another way, without going through mp3wrap.
    cat file list… | ffmpeg -f mp3 -i – -acodec copy outfile.mp3
    And then the id3 copy command.”

    Shelby, this works perfectly no need to install unnecessary things, Thanks Lex

  • The “cat file list… | ffmpeg -f mp3 -i – -acodec copy outfile.mp3” version does not work for me but mp3wrap works ok.

  • after some looooong trial and error, found the following way to join VBR mp3s into one with _correct_ _basic_ headers/size, understood same by multiple readers (mp3info, eyeD3, mad):
    $ cat infiles..* > cat.joined.mp3 #join files
    $ cutmp3 -a 0:0 -b 99999:0 -i cat.joined.mp3 -O cut3.cat.joined.mp3 #clean out stuff
    $ vbrfix cut3.cat.joined.mp3 -O vbr.cut3.cat.joined.mp3 #fix

    u may prepend a step to remove any id3*tags, say by $ id3v2 -D infiles.. ,
    and append a step to put correct id3 tags on final result

    well, it depends on input materials – YMMV… Reencoding is always a final option.

    here all utils i tried:

    utils for info/reading:
    eyeD3 files..
    mp3info -x -r a infiles..

    utils for joining (first 3 work ~equivalent):
    cat infiles.. > result
    mpgtx -j -o result infiles..
    qmp3join -o result infiles.. (from quelcom package)
    mp3wrap result infiles..

    utils for cleanup/fix:
    cutmp3 #actualy an editor but with a nifty feature to skip crap)
    id3v2 -D #remove all id3* tags
    ffmpeg -f mp3 -i infile -acodec copy outfile #copies too much through, put extra id3v2.4 hdr
    mp3val -f #sometimes enough
    vbrfix
    these two were not of much use:
    mp3check -B -e
    checkmp3

    vbrfix is from http://home.gna.org/vbrfix/ https://gna.org/svn/?group=vbrfix – compiled from source, may need fix of the input-options-checking. All else is ubuntu packages.

    have fun

  • Thanks very much for posting. One small change for Ubuntu Linux 10.04 ,I use id3v2 instead of id3cp — there’s no id3lib from the apt-get as far as I can tell. And I already had id3v2.

    With this option it’s not automatic so there is surely something better for those who try harder.

  • Thanks! This was VERY helpful for a series of large merges. Used the technique here along in a nice bash script.