68

As it was suggested here I am using cat command to concatenate several .mp3 files into one .mp3 file.

Imagine, I have following .mp3 files in the current folder:

001001.mp3 001002.mp3 001003.mp3 001004.mp3 001005.mp3

or, like this:

096001.mp3 096002.mp3 096003.mp3 096004.mp3

I need to concatenate these .mp3 files in there ascending sequence, i.e. 001001.mp3+001002.mp3+001003.mp3+etc.

In order to join these .mp3 files into one I am executing following command in the current folder:

cat *.mp3 > final.mp3

I tested the final .mp3 file and it is what I am expected, but I need to be sure that above command picks files in there ascending sequence.

Can I be sure that above command always concatenates files in the ascending sequence?

Thank you Sir!

Bakhtiyor
  • 12,254
  • Would you say why you want to do this? If it is to create a single-file audiobook, you should check out https://github.com/sandreas/m4b-tool – Adam Monsen Apr 24 '22 at 00:06

9 Answers9

81

cat is not the right tool for this job. The MP3 format has all sorts of junk that can lurk at the front and end of the file and this needs to be strippe out. mp3wrap is what you want. It will exclude any metadata in the files and stick the audio together.

sudo apt-get install mp3wrap
mp3wrap output.mp3 *.mp3

Before you do that, run ls *.mp3 to check that they're in the correct order. When I originally wrote this answer (over six years ago!) wildcard globs apparently didn't behave well but I think they do now.

You might need to rename the files if for example, they are numbered but aren't zero-padded, {1-11}.mp3 would be sorted by 1 10 11 2 3 4 5 6 7 8 9. This can be fixed easily.

Oli
  • 293,335
  • 2
    Right - cat will mangle the mp3 file format by "creating an array" (as it were) of mp3 files within a single mp3 file. But it seems to work if the player software is smart enough. –  Jan 08 '11 at 01:25
  • 1
    This worked better for me, because it handles mp3s with spaces in the filename: find . -maxdepth 1 -iname '*.mp3' -print0 | sort -z | xargs -0 mp3wrap output.mp3 – bmaupin Jul 31 '14 at 01:25
  • 1
    @bmaupin A fair point but I'm not sure why I wasn't just using Bash's wildcard expansion. *.mp3 should suffice (and works with spaces). – Oli Jul 31 '14 at 07:49
  • Available for mac from brew package manager! $ brew install mp3wrap. – Rubens Apr 04 '16 at 16:14
  • 1
    In bash, *.mp3 is guaranteed to preserve alphabetical order. If your files are named 09.mp3, 10.mp3, 11.mp3, that's probably what you want. If your files are named 9.mp3, 10.mp3, 11.mp3, that's probably not what you want, but you can do \ls *.mp3 | sort -n``. – Jo Liss Jul 23 '16 at 17:48
  • With 295 files, I get:

    mp3wrap output.mp3 *.mp3 Mp3Wrap Version 0.5 (2003/Jan/16). See README and COPYING for more! Written and copyrights by Matteo Trotta - <matteo.trotta@lib.unimib.it> THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY! USE AT YOUR OWN RISK! Error: too many files to wrap! Solution seems to be to go hierarchical: http://mp3wrap.sourceforge.net/faq.html#q6

    – vishvAs vAsuki Oct 26 '17 at 23:10
  • @vishvAsvAsuki With that error (Error: too many files to wrap!), you could try building it up in chunks. Maybe concatenate chunks of 10 mp3 files, then concatenate those together. – wjandrea Dec 23 '17 at 01:44
  • Regarding sorting, if you know the filenames, you could use brace expansion, e.g: mp3wrap output.mp3 {1..11}.mp3 – wjandrea Dec 23 '17 at 01:54
  • awesome solution. anyway to normalize the songs too? – ar2015 Jun 24 '19 at 11:24
45

mp3wrap seems to be a decent enough solution, but when I played the resulting file, the timestamp wasn't correct. It seems like mp3wrap is best used when you're joining mp3s into a file that you know you'll want to split later.

I simply wanted to permanently concatenate some mp3s together. I ended up using ffmpeg to concatenate the files:

  1. First, install ffmpeg

    • Ubuntu 15.04+

      sudo apt install ffmpeg
      
    • Ubuntu 14.10 and below
      Go to http://ffmpeg.org/download.html, download one of the static builds, untar it, and copy to /usr/local/bin

  2. ffmpeg -i "concat:file1.mp3|file2.mp3" -acodec copy output.mp3

More info: https://trac.ffmpeg.org/wiki/Concatenate#protocol
https://superuser.com/a/314245

bmaupin
  • 4,930
  • ffmpeg is missing, but avconv replaces it: http://askubuntu.com/questions/432542/is-ffmpeg-missing-from-the-official-repositories-in-14-04 – muru Jul 31 '14 at 02:00
  • 2
    Yeah, I used avconv for a while, but then I needed some features that ffmpeg had and avconv didn't, and I haven't used it since. These days it feels like a waste of time checking to see if avconv has the features I need when I already know ffmpeg does. – bmaupin Jul 31 '14 at 13:05
  • Please feel invited to join this bug report discussion. – orschiro Jan 25 '16 at 08:41
  • I've tried this and it doesn't work great; try running output.mp3 thru a mp3val and you'll see all sorts of errors that will create playback issues in Chrome64+ (see https://bugs.chromium.org/p/chromium/issues/detail?id=794782 and https://bugs.chromium.org/p/chromium/issues/detail?id=806601#c10) – The Onin Feb 04 '18 at 05:45
  • awesome solution. anyway to normalize the songs too? – ar2015 Jun 24 '19 at 11:25
  • This should be the marked answer – Csaba Toth Feb 12 '21 at 20:39
  • @ar2015 Normalization should be a pre-processing step. loudgain mp3gain or replaygain analyzes your songs and only adds a meta tag which signals the audio player how to modify the volume, so that avoids re-encoding. Since here you concatenate files that would not be beneficial so I'd go with normalizeaudio which re-encodes your files. That might have some quality impact, but the meta-tagging would not work if you concatenate. You cannot use the mentioned techniques as post-processing, because it calculates a gain modification per whole files. – Csaba Toth Feb 12 '21 at 20:44
  • 1
    Works nicely for me. With a lot of files in the directory, to get a |-separated list of filenames that I could paste into the ffmpeg command line, I used the following: python -c 'import glob; print("|".join(sorted(glob.glob("*.mp3"))))' – alani Jun 22 '22 at 08:06
9

Most people are suggesting mp3wrap, but it has downsides: the file generated does not report its full length in some players, and then you cannot seek past that point.

mp3cat does the job fast and well, as far as I can tell. It can be downloaded in http://mulholland.xyz/dev/mp3cat/, and used as follows:

mp3cat file1.mp3 file2.mp3 file3.mp3

another possibility would be sox

sudo apt-get install sox libsox-fmt-mp3
sox file1.mp3 file2.mp3 target.mp3

but it seems very slow (possibily it decodes and reencodes the audio?)

josinalvo
  • 6,947
  • 5
  • 37
  • 51
8

As previously suggested, mp3wrap is a good solution. It may not work all of the time though. As far as I know, mp3wrap assumes that all the input files have the same characteristics such as VBR vs CBR, bitrate, and so on. If this assumption isn't met, it is likely to fail. In that case, the only solution would be to decode all the mp3files to a raw format like .wav, concatenate them with a program like sox and finish by re-encoding all to mp3.

loevborg
  • 7,282
  • Note that re-encoding mp3 files (also know as "transcoding") will result in a quality loss. I would not recommend it. – Martin Tournoij May 31 '19 at 03:51
  • @MartinTournoij Then what would you recommend as a solution where the files' encodings are too different for mp3wrap to handle? – Mr Lister Dec 09 '21 at 13:19
5

It does an alphabetical sort based on single characters. That means that "01" comes before "1", since nought has a lower value than one.

Here's an example. I've got a directory with files named 1, 2, 3, 04, 05, and 06. They are text files that contain their own file names:

test$ cat *
04
05
06
1
2
3

So, yes it will; but you need to make sure that all your files are 'padded' properly.

This nifty line of bash script will let you visually compare the file names, making it very easy to spot any mistakes:

for f in $(ls); do printf "%05s\n" "$f"; done

It's output will look like this:

   04
   05
   06
    1
    2
    3

If they aren't, you will need to pad the file names: Bash script to pad file names on StackOverflow explains how to do it.


Edit. Vote for Oli's answer, it's much better. :P
I'll leave mine because it add something, but you should use his solution,

mp3wrap output.mp3 `ls -1 *.mp3 | sort`

Keep in mind that sort will still sort things in the way I described above, you will still need to pad file-names if they aren't equal in length.

  • "Lexical" or "Lexicographic" describes this sort order better than "alphabetical". "Alphabetical" is not well defined, and may be used to describe sort orders that treat spaces and other punctuation as if they did not exist. – mc0e Jul 02 '21 at 15:54
4

I found the online service audio-joiner to work very well and preserve the correct time-stamp (contrary to mp3wrap).

orschiro
  • 13,317
  • 17
  • 87
  • 161
1
perl -E'say qq(file '\''$_'\'') for <*>' *   > file.list   && ffmpeg -f concat -safe 0 -i file.list -c copy mybigfile.mp3
  1. copy this line into a terminal (where you have perl and ffmpeg available) after changing to the folder with all the files you want to concatenate
  2. it will cause an error aboute "file.list" (because "file.list" will be written into the "file.list"-file) but nevermind, it still works
  3. all in all this just writes the foldercontent into the "file.list"-file and formats it.
  4. after that it will merge the files into one mp3 file with ffmpeg
MacMartin
  • 353
1

Let me summarize the other answers:

  • mp3wrap joins mp3 files into a special "concatenated mp3 file" format, which is faster because it doesn't require re-encoding, but has the disadvantage of not being a normal mp3 file with a length and so on.

  • ffmpeg -i "concat:file1.mp3|file2.mp3" -acodec copy output.mp3 is cumbersome to type, but should work well, as it's a standard command to use for joining video files for example. However, in my testing it introduces glitches at the input file transitions.

  • sox file1.mp3 file2.mp3 target.mp3 should work great, certainly much easier to type, but it also introduces glitches in my testing.

  • Even when I use sox to first convert each .mp3 file to .wav, and then sox file1.wav file2.wav target.wav, the output contains audible glitches.

  • However, if I use mpg123 to decode the mp3 files to .wav, then I can join the .wav files with Sox and not hear glitches. This suggests that the problem with sox (and maybe ffmpeg) is in the decoding of the mp3 files.

  • mp3cat apparently works great but I was too lazy to test it, as it isn't found in my distribution's repository

It's curious that no one mentioned mpg123 yet. Although troubled by security and license issues early in its 20+ year history, it is one of the original mp3 players and should be fairly stable. Since it automatically concatenates its mp3 arguments (in a glitch-less fashion) before sending them to the sound card, we just have to use the -w option to tell it to output to a Wave file instead. Then lame encodes this back to mp3. Here's a script which accomplishes this:

#!/bin/bash
FILES=("$@")
OUTPUT=$(mktemp -t mp3-concat.XXXXXX.mp3 -u)
WAVE=$(mktemp -t mp3-concat.XXXXXX.wav -u)
mpg123 -w $WAVE ${FILES[@]}
lame $WAVE $OUTPUT
cat $OUTPUT
rm $WAVE $OUTPUT

Usage example:

$ mp3-concat file1.mp3 file2.mp3 > joined.mp3
0

I love bmaupin's comment, yet it could produce natural sort of (version) numbers by using sort -V:

find . -maxdepth 1 -iname '*.mp3' -print0 | sort -Vz | xargs -0 mp3wrap ../tmp.mp3

and you don't have to deal with special characters like ' " () [] {} in the filename.

zhoux
  • 1