92

I have a load of audio files (about 1000) which I want to convert from m4a to mp3 so I can use play them on a CD player which has a USB port.

I tried doing something simple like: ffmpeg -i FILE.m4a FILE.mp3 but this seems to reduce the bitrate to a very low value, which isn't what I want.

Similarly I don't want to convert using a constant bitrate, such as 320k, because some of the files I am converting are 320k m4a's and some are as low quality as 96k m4a's.

It seems to make no sense to force 320k, since some files will become many times larger than they need be. Similarly it makes no sense to destroy all my 320k files by converting them to something much lower than 96k. (At the moment, the files are being converted to about 50k.)

Does anyone know how I can do this? What I really want to do is tell ffmpeg to convert all m4a files in a directory into mp3's while retaining the current audio quality as best it can. (Of course there is likely to be some extra losses from converting from lossy to lossy file formats, above that which would be expected when converting from a lossless to lossy format.)

Thanks for your help. If this isn't possible, is there some sort of script which might detect the required quality as it converts files individually?

PS: I am working on an intel Mac, but also have a Ubuntu box.

7 Answers 7

118

Use Variable Bit Rate (VBR)

You can use the -q:a option in ffmpeg to create a variable bitrate (VBR) MP3 output:

ffmpeg -i input.m4a -c:v copy -c:a libmp3lame -q:a 4 output.mp3

What -q:a values to use

From FFmpeg Wiki: MP3:

Control quality with -q:a (or the alias -qscale:a). Values are encoder specific, so for libmp3lame the range is 0-9 where a lower value is a higher quality.

  • 0-3 will normally produce transparent results
  • 4 (default) should be close to perceptual transparency
  • 6 usually produces an "acceptable" quality.

The option -q:a is mapped to the -V option in the standalone lame command-line interface tool.

You'll have to experiment to see what value is acceptable for you. Also see Hydrogen Audio: Recommended LAME Encoder Settings.

Encoding multiple files

In Linux and macOS you can use a Bash "for loop" to encode all files in a directory:

$ mkdir newfiles
$ for f in *.m4a; do ffmpeg -i "$f" -codec:v copy -codec:a libmp3lame -q:a 2 newfiles/"${f%.m4a}.mp3"; done
2
  • 2
    Okay thanks, the commands worked but I don't think it is doing what I wanted. I've set -q:a 0, which is doing what I expected for higher quality m4a's (producing large files with higher vbr rates) but lower quality files I think are still being converted into large files, with vbr's of about 250-260 kb/s. I would have expected the files which are about 96k in m4a format to be converted to a similar bitrate with the lame encoder in vbr mode. I'm assuming I haven't quite understood how the vbr encoding mode works? Jan 21, 2014 at 22:42
  • 1
    Another variant for multiple files without for loops, but using ls, pipes and grep: ls | egrep \.m4a$ | sed -r "s/^(.+)\.m4a/ffmpeg -i '\1.m4a' -codec:v copy -codec:a libmp3lame -q:a 0 '\1.mp3'/" The output are the ffmpeg commands, when it looks good to you, add | sh at the end to execute them all.
    – xaedes
    Dec 2, 2020 at 12:02
42

Use FFmpeg command line version.

MP3 is officially discontinued (from 2017) and obsolete codec (I hope it will become dead as soon as possible). Interent-world has already switched to AAC (more efficient & quality codec), and everyone should use that (instead of mp3):

ffmpeg -i filenameee.m4a -acodec copy output.aac

Update: Even though I don't recommend that, unfortunately, some low-quality and obsolete hardwares' developers still produce appliances which use mp3 and some people still request for that format... Agrhhhh, here it is:

ffmpeg -i filenameee.m4a -acodec libmp3lame -ab 256k output.mp3
6
  • 6
    You write "MP3 is officially discontinued (from 2017), obsolete and dead codec" bur the link you give says "Some weeks ago, we updated our website with information about the end of the mp3 licensing program by Technicolor and Fraunhofer. So, does this mean that mp3 is really dead now, as we have read often in the last few days? Of course not! mp3 is a phenomenon, which changed our way of consuming music forever, and is very much alive in 2017. The licensing program coming to an end is due to the fact that the last patent included in the program expired. In no way does that mean that the
    – barlop
    Dec 30, 2019 at 0:11
  • 1
    (cntd) "usage permit ends. The only ones deciding on the “death” of mp3 will be the users, who might switch to more modern audio formats at some point, such as AAC, which is included in almost every smartphone today." <-- end of quote from your link.
    – barlop
    Dec 30, 2019 at 0:11
  • This worked to batch convert on windows cmd: FOR %I in (*.m4a) DO ffmpeg -i %I -acodec libmp3lame -ab 128k "%I.mp3" Sep 4, 2021 at 8:28
  • 1
    @rlittles , if that can't be called a discontinual of a product development & support, then let me know your definition of what else should be called. I nowhere said that "mp3 supported appliances" are discontinued.
    – T.Todua
    Jul 27 at 15:50
  • 1
    @T.Todua file formats aren't physical products that can be discontinued... the creators of the format posting some legalese on their website doesn't mean that it's suddenly against the laws of physics for me to create a new MP3 file. It's certainly worth noting in your answer that there are many better formats than MP3 out there though, as that is correct and helpful.
    – rlittles
    Jul 28 at 7:02
5

This worked for me:

ffmpeg -i testing.m4v -b:a 192K -vn testing.mp3
4

Using existing answers as a basis I wrote a bash script which should do exactly what the question asks on an Ubuntu machine (tested on 16.04). You'll need to install avprobe which is in the libav-tools package. You can install avprobe with the following command

sudo apt-get install libav-tools

NOTE: It would be wise to make a backup of any folder in which you run this script as well as make your best effort to read and understand what it is doing before running.

I have not tested very rigorously so YMMV.

#!/bin/bash

mkdir "mp3s"
for f in *.m4a;
do
    bitrate=$(avprobe "${f}" 2> >(grep bitrate) | sed 's/^.*bitrate://g' | sed 's/[^0-9]*//g')
    bitrate="${bitrate}K"
    new_filename=$(echo "${f}" | sed 's/.m4a$/.mp3/g')
    ffmpeg -y -i "${f}" -acodec libmp3lame -ab "${bitrate}" "mp3s/${new_filename}"
done
2
  • 2
    avprobe for windows builds.libav.org/windows/release-gpl can also use mediainfo to show bitrate.. The second answer shows this is the kind of line you want.. like what you did but this shows it more raw ffmpeg -i inputfile.m4a -acodec libmp3lame -ab 256k outputfile.mp3 and as you have, one can apply that to multiple files.
    – barlop
    Dec 30, 2019 at 0:34
  • Note: avprobe comes with the libav library, which is considered abandonware, since its last stable release was in February 2018 (there have been a few updates in early 2019 on their git). You can replace avprobe with ffprobe — after all, it comes with ffmpeg — but the actual probe command requires some radical changes, as ffprobe has some weird way of extracting and presenting data. See my own suggestion below. Jul 30 at 17:40
2

@jbowman's answer above is (at the time of writing this) the only one that truly addresses the OP's main issue, which is preserving the bit rate in the original M4A — neither more nor less — on the resulting MP3. His provided script does exactly that, relying on avprobe to extract the bit rate from the M4A before doing the actual conversion to MP3.

Sadly, avprobe comes with the libav library, which is considered abandonware, since its last stable release was in February 2018 (there have been a few updates in early 2019 on their git).

The good news is that you can replace avprobe with ffprobe — after all, it comes with ffmpeg, so you don't need to add an additional package for that. ffprobe is, at best, tricky to coerce into the proper format, but, after much googling, I sort of found a solution which does, indeed, extract the correct bit rate (at least for the first stream in the M4A — if you have many, this will probably not work):

ffprobe -v quiet -of flat=s=_ -show_entries format=bit_rate <source filename>

Which should result in something like:

format_bit_rate="324440"

This requires a slightly different (and simpler) way to parse, so I wrote my own script to do that, with a twist: in my case, I have lots of albums on my Mac, some of which are in M4A, and which require converting to MP3 for my low-end car audio player. There are at least a gazillion tools that do that, some even just GUI-based front ends to ffmpeg but nothing like 'being in control' and do it from the bash!

So, copy & paste the script below, put it inside a file, add the required chmod +x file-name-chosen-for-the-script, and let it roll, by invoking it with a single parameter, which is the directory where your collection of M4A files is. Just like @jbowman, I've placed the converted files inside a mp3s/ directory, but with a twist: it adds the album name as an extra subdirectory (basically, the last element of the directory fed to the script) — so everything will be grouped together again by album name :)

#!/usr/bin/env bash

# Takes a directory as parameter and converts everything inside it
# from M4A to MP3, respecting the bitrate, and creating a directory
# for the converted files.
#
# Based on https://superuser.com/a/1211902/127382 by @jbowman (2017)

if [ -d "${1}" ]
then
        echo "Doing conversions from directory \"${1}\"..."
else
        echo "Source directory not found! Please add it as a parameter, e.g.:"
        echo "  ${0} path/to/album/containing/mp3/files"
        exit 1
fi

# Get the album's name from the first parameter
ALBUM_PATH="${1}"
ALBUM=$()basename "${ALBUM_PATH}")

echo "Attempting to process album \"${ALBUM}\" in source directory
\"${ALBUM_PATH}\"..."

# Create a directory for this run
mkdir -p "mp3s/${ALBUM}"

# Check if mkdir succeeded
if [[ $? -ne 0 ]]
then
        echo "Could not create directory mp3s/${ALBUM} for output. Exiting..."
        exit 2
fi

# Loop across the filnames in that directory that end in *.m4a, invoke
# ffprobe to extract the bitrate, then use ffmpeg to convert it into MP3
# and place the result inside the mp3s/${ALBUM}/ directory:
for f in "${ALBUM_PATH}"/*.m4a
do
        echo "Before ffprobe... testing ${f}"
        bitrate=$(ffprobe -v quiet -of flat=s=_ -show_entries format=bit_rate "${f}" | sed 's/[^0-9]*//g')
        new_filename=$(basename "${f}" .m4a).mp3
        echo "Reading ${f} and writing to mp3s/${ALBUM}/${new_filename} with bitrate ${bitrate}..."
#       echo ffmpeg -y -i "${f}" -codec:a libmp3lame -b:a "${bitrate}" -q:a 2 "mp3s/${ALBUM}/${new_filename}"
        ffmpeg -y -i "${f}" -codec:a libmp3lame -b:a "${bitrate}" -q:a 2 "mp3s/${ALBUM}/${new_filename}"
done

To-do: allow multiple directories as a command-line parameter, thus allowing a long batch to be run across the entire music library (1000s of songs!) and organise them properly according to their album names. The current script just works for one directory (no globbing directory names, or the results will be unpredictable — that's a fair warning!).

Caveat: some M4As really have a very high bit rate, and ffmpeg complains a bit about those — after all, the MP3 specs define the maximum bitrate as 320 Kbps, with 192 Kbps (variable bit rate) being in practice the 'best' a human can hear with the kind of lossy compression provided by MP3. Although LAME can (allegedly) go up to 640 Kbps, such MP3s wouldn't be able to be played by ordinary, low-tech devices anyway.

ffmpeg still does the conversion, though, and caps it at 320 Kbps (as per specs)

Script also available as a gist.


Update: Thanks to @mashuptwice for reviewing & correcting some details on the script (see comments)

8
  • 1
    I've edited the shebang in your script as /usr/local/bin/ is a pretty uncommon path for bash to reside in. Jul 30 at 17:51
  • 1
    Also backticks are deprecated, use $(command) instead Jul 30 at 17:55
  • 1
    Another thing is that by posting on SE sites, you share your work under the creative commons license. There is no need to specify a different license, except maybe for posting it on different sites. Jul 30 at 18:01
  • 1
    In case of 1) you should rather use #!/usr/bin/env bash, that way your script is compatible over a wide variety of OS' without any modifications. Jul 31 at 2:17
  • 1
    @mashuptwice all right, #!/usr/bin/env bash it is :) Thanks again. Aug 1 at 10:46
-1

If you work at 48 KhZ and at least 24-bit in the input file (from a file editor i.ex.) -q:a 0 will reproduce the 20 kHz at no so large file as flac... But low quality audio unless you reduce the noise from high frequencies (hiss) by dithering or nose reduction audio editors, created in conversions, that will be reproduced, then will use data file as a inaudible HF, but expensive noise.

0
-1

My m4a file wouldn't convert with ffmpeg at all(generating unreadable mp3 file roughly the same size as m4a). For it me it worked to use vlc for conversion:

  1. Media ->
  2. Convert\Save ->
  3. Select file ->
  4. Convert file ->
  5. Choose profile "Audio mp3) ->
  6. press wrench button near profile ->
  7. Select Audio codec and edit 128kbps to 320 ->
  8. press save ->
  9. Enter output filename ->
  10. Start

Not the answer you're looking for? Browse other questions tagged or ask your own question.