wiki:Howto/CreateM4bAudioBooks

Create M4B Audio Book for Apple iPod, or convert M4B to MP3

Here’s how to convert M4B audiobooks (used by Apple iPod) to MP3, and how to create an M4B from one or more MP3s. M4B is an M4A file with bookmarks so the player can remember where you left off listening and resume from that point later.

M4B to MP3

For a single file:

ffmpeg -i <infile.m4b> -acodec libmp3lame -ar 22050 <outfile.mp3>

For a bunch of M4B files:

for m4b in $(ls -1 *.m4b); do ffmpeg -i $m4b -acodec libmp3lame -ar 22050 ${m4b}.mp3; done

Single MP3 to M4B

A standard Ubuntu installation of ffmpeg doesn't use the libfaac library because of legal restrictions in some jurisdictions, so faac needs to be installed from the Universe repository:

sudo apt-get install faac

Now use a first-in, first-out, pipe to take pulse-code modulation (PCM) output from ffmpeg and feed it to faac. To make it a little simpler to manage I use a small script that I edit for each file.

Here's an example. I have in the Media directory the file "Barack Obama - The Audacity of Hope.mp3" (6 hours 10 minutes)

Notes:

  1. The book was bought from Audible and played using wine + iTunes because of the Digital Restrictions Management and captured from the audio card PCM loop-back input using Audacity (16000Hz, 16-bit, stereo) and saved to a 128k, 16000Hz joint-stereo MP3. I want to be able to listen to the file from the iPod nano but wine + iTunes can't (as yet) successfully access the iPod. iTunes can however authorise the PC for DRM playback and play the file (The Audible player won't successfully authorise).
  2.  With my patch for wine Audible player can now authorise tracks successfully.
#!/bin/bash
# mp3tomb4
BITRATE="128k"
FREQ="16000"
SAMPLESIZE="16"
CHANS="2"
TITLE="The Audacity of Hope"
AUTHOR="Barack Obama"
GENRE="Spoken Word"
YEAR="2006"
PIPE="pipe.pcm"
mkfifo $PIPE
ffmpeg -i "$AUTHOR - $TITLE.mp3" -f s16le -ac $CHANS -ar $FREQ -ab $BITRATE  -acodec pcm_s16le -y $PIPE </dev/null &
faac -R $FREQ -B $SAMPLESIZE -C $CHANS -X -w -q 80 --artist "$AUTHOR" --album "$TITLE" --title "$TITLE" --track "1" --genre "$GENRE" --year "$YEAR" -o "$AUTHOR - $TITLE.m4b" $PIPE
rm $PIPE

I edit and save the script mp3tom4b and make it executable:

chmod a+x mp3tom4b

Then, in the same directory as the input file, I call the script. Here's a capture from the terminal whilst it is encoding:

mp3tom4b
FFmpeg version r11872+debian_3:0.svn20080206-12ubuntu1~ppa1h, Copyright (c) 2000-2008 Fabrice Bellard, et al.
  configuration: --enable-gpl --enable-pp --enable-swscaler --enable-x11grab --prefix=/usr --enable-libgsm --enable-libtheora --enable-libvorbis --enable-pthreads --disable-strip --enable-libdc1394 --enable-shared --disable-static
  libavutil version: 49.6.0
  libavcodec version: 51.50.0
  libavformat version: 52.7.0
  libavdevice version: 52.0.0
  built on Sep 29 2008 01:41:27, gcc: 4.2.3 (Ubuntu 4.2.3-2ubuntu7)
Freeware Advanced Audio Coder
FAAC 1.26.1 (Jan 22 2008) UNSTABLE

Input #0, mp3, from 'Barack Obama - The Audacity of Hope.mp3':
  Duration: 06:21:23.3, start: 0.000000, bitrate: 127 kb/s
    Stream #0.0: Audio: mp3, 16000 Hz, stereo, 128 kb/s
Output #0, s16le, to 'pipe.pcm':
    Stream #0.0: Audio: pcm_s16le, 16000 Hz, stereo, 512 kb/s
Stream mapping:
  Stream #0.0 -> #0.0
Press [q] to stop encoding
Quantization quality: 80
Bandwidth: 8000 Hz
Object type: Low Complexity(MPEG-4) + M/S
Container format: MPEG-4 File Format (MP4)
Encoding pipe.pcm to Barack Obama - The Audacity of Hope.m4b
 frame | elapsed | play/CPU
 357500 |   372.8 |   61.37x invalid new backstep 55380.4 bitrate= 512.0kbits/s    
size= 1430201kB time=22883.2 bitrate= 512.0kbits/s    
video:0kB audio:1430201kB global headers:0kB muxing overhead 0.000000%
 357552 |   372.9 |   61.37x 

ffmpeg runs in the background and starts when faac connects to the pipe. faac reports its progress as it encodes. The final results:

ls -lh
-rw-r--r--  1 tj tj 160M 2008-12-29 08:35 Barack Obama - The Audacity of Hope.m4b
-rw-r--r--  1 tj tj 350M 2008-12-29 06:57 Barack Obama - The Audacity of Hope.mp3

Several MP3 files to M4B

The more involved bit is to create an M4B audiobook from a bunch of MP3 files, so I have an automated script.

First thing is to ensure all the MP3 files you want to include in ONE M4B have sequentially numbered names:

filename-01.mp3
filename-02.mp3

or, if you want to create the M4B from more than 100 M4B files:

filename-001.mp3
filename-002.mp3

etc.

Copy and save the script below to the file create_audiobook and make it executable:

chmod 755 create_audiobook

For each M4B audiobook optionally set the tags using environmental variables:

export AUTHOR="The Author"
export TITLE="Some Book"
export YEAR="2007"

Make sure you’ve got plenty of free space on the mount point the source files are on. If you need to have the HUGE temporary files put on another mount point with plenty of space (Allow for more than 3GB of free space) then set the TMP environmental variable to point to a location that has plenty of space and allows users to create/delete files:

export TMP=/tmp

By default, TMP is set to the current directory if not specified like this.

Now run the script, passing the output filename and the input file mask. Here’s two possible usages:

create_audiobook "Terry Pratchett - Colour of Magic" "Terry Pratchett - Colour of Magic - *.mp3"
create_audiobook "Terry Pratchett - Colour of Magic" *.mp3

Note the quotes around the two parameters that allows spaces in filenames.

#!/bin/bash
# create ipPod audiobook from group of sequentially named MP3 files
# Set environmental variables for track ID3 tags
# AUTHOR, TITLE, YEAR
# usage: create_audiobook
#
# Pass output filename on command-line with no extension, and use quotes if there are spaces in filenames
# e.g. create_audiobook "Terry Pratchett - Colour of Magic" *.mp3
# e.g. create_audiobook "Terry Pratchett - Colour of Magic" “Terry Pratchett - Colour of Magic - *.mp3″

if [ "x$TMP" == "x" ]; then
    TMP=.
fi

echo "Args: $@"
echo "Author: $AUTHOR"
echo "Title : $TITLE"
if [ "x$1" != "x" ]; then
    filename=$1
    if [ "x$2" != "x" ]; then
        echo "Output: $filename"
        filelist="$(echo "$2" | sed -e ’s/ /\\ /g’)"
        echo "Filelist : $filelist"
        echo "Writing HUGE temporary files to $TMP"
        # Use a sub-shell to preserve spaces in filenames passed to mp3wrap
        sh -c "mp3wrap -v \"${TMP}/${filename}\" ${filelist}"
        if [ $? = 0 ]; then
            mplayer -vc null -vo null -ao "pcm:nowaveheader:fast:file=${TMP}/${filename}.pcm" "${TMP}/${filename}_MP3WRAP.mp3" 2>&1 \
             | tee ${TMP}/create_audiobook_mp.log
            # example from log
            # AO: [alsa] 44100Hz 2ch s16le (2 bytes per sample)
            AUDIO="$(egrep 'AO:' ${TMP}/create_audiobook_mp.log)"
            echo "Audio Format: $AUDIO"
            RATE=$(expr "$AUDIO" : 'AO: .* \(.*\)Hz.*')
            CHANS=$(expr "$AUDIO" : 'AO: .* .*Hz \(.*\)ch.*')
            SAMPLESIZE=$(expr "$AUDIO" : 'AO: .* .*Hz .*ch s\([0-9]\+\).. .*')
            echo "ENCODING: $RATE Hz, $CHANS channels, $SAMPLESIZE bit sample size"
            /usr/bin/faac -R $RATE -B $SAMPLESIZE -C $CHANS -X -w -q 80 -artist "$AUTHOR" -album "$TITLE" -title "$TITLE" \
              -track "1" -genre "Spoken Word" -year "$YEAR" -o "${filename}.m4b" "${TMP}/${filename}.pcm"
            rm -f "${TMP}/${filename}.pcm"
            rm -f "${TMP}/${filename}_MP3WRAP.mp3"
            rm -f "${TMP}/create_audiobook_mp.log"
            echo -e "\n\nCreated ${filename}.m4b"
        fi
    fi
fi