Voxbound / Guides / Merge M4B files
How to merge M4B files into one audiobook on Windows
FFmpeg, which is free, can join M4B files without re-encoding them. In testing, though, a plain join kept no chapters and no title, author or album tags, and kept the cover only with one extra option. This page shows what a join loses, a PowerShell script that writes the chapters back, what to do when the parts don't match, and how Voxbound does the same job.
Before you start
The commands need FFmpeg, which includes ffprobe; our MP3 to M4B guide shows how to install it. Put the parts in one folder, with no other .m4b files in it, and open Windows PowerShell there. The commands write the finished book to the folder above, so it isn't picked up as another part.
This page covers M4B files that aren't copy-protected. Protected ones, such as Audible's .aax downloads, can't be joined with the commands here or with Voxbound.
To see what a part holds, list its chapters:
ffprobe -v error -show_chapters -of compact "part1.m4b"
Each chapter prints as one line with its start time, end time and title. No lines means the part has no chapters.
What a plain join loses
FFmpeg's concat option joins files without re-encoding them. It reads a list of files, so write the list first, then join. File names must be plain English letters and digits here, and Get-ChildItem lists them alphabetically, so part10 would come before part2; name the parts 01, 02 and so on. The script in the next section has neither limit.
Get-ChildItem *.m4b | ForEach-Object { "file '$($_.Name)'" } | Set-Content -Encoding ascii list.txt
ffmpeg -f concat -safe 0 -i list.txt -map 0:a -map 0:v? -c copy -disposition:v attached_pic "..\joined.m4b"
In testing, on three parts that each had two chapters, a title, author and album tag, and a cover:
- The audio is joined, and the length is the sum of the parts.
- The chapters are gone: the check above, run on the joined file, prints no lines.
- The title, author and album tags are gone.
- The cover is kept, taken from the first part, only because of
-disposition:v attached_pic. Without that option, FFmpeg stopped with "codec not currently supported in container" and wrote no usable file. With-map 0:aalone, the join worked and had no cover.
A book joined this way plays as one long track in every player; our guide to chapters that don't show covers that symptom.
Join the files and keep the chapters
FFmpeg writes chapters and tags into the joined file if you give it a metadata file. This script builds that file from the parts. It reads each part's chapters and moves them to where that part starts in the joined book, and a part with no chapters gets one, named after its file. Set $book and $author first, then paste it into Windows PowerShell as one block, in the folder that holds the parts:
[Console]::OutputEncoding = [Text.Encoding]::UTF8
$out = "..\book.m4b"
$book = "My Book"
$author = "Some Author"
$esc = { param($t) $t -replace '([=;#\\])', '\$1' }
$utf8 = New-Object Text.UTF8Encoding $false
$files = Get-ChildItem *.m4b | Sort-Object { [regex]::Replace($_.Name, '\d+', { $args[0].Value.PadLeft(10, '0') }) }
[IO.File]::WriteAllLines("$PWD\list.txt", [string[]]($files | ForEach-Object { "file '" + $_.Name.Replace("'", "'\''") + "'" }), $utf8)
$meta = ";FFMETADATA1`ntitle=$(& $esc $book)`nalbum=$(& $esc $book)`nartist=$(& $esc $author)`n"
$offset = 0
foreach ($f in $files) {
$info = ffprobe -v error -show_chapters -show_format -of json $f.FullName | ConvertFrom-Json
$length = [Math]::Round([double]$info.format.duration * 1000)
$n = 0
foreach ($c in @($info.chapters)) {
$n++
$title = if ($c.tags.title) { $c.tags.title } else { "$($f.BaseName) $n" }
$meta += "[CHAPTER]`nTIMEBASE=1/1000`nSTART=$($offset + [Math]::Round([double]$c.start_time * 1000))`nEND=$($offset + [Math]::Round([double]$c.end_time * 1000))`ntitle=$(& $esc $title)`n"
}
if ($n -eq 0) {
$meta += "[CHAPTER]`nTIMEBASE=1/1000`nSTART=$offset`nEND=$($offset + $length)`ntitle=$(& $esc $f.BaseName)`n"
}
$offset += $length
}
[IO.File]::WriteAllText("$PWD\chapters.txt", $meta, $utf8)
ffmpeg -v error -f concat -safe 0 -i list.txt -i chapters.txt -map 0:a -map 0:v? -map_metadata 1 -map_chapters 1 -c copy -disposition:v attached_pic $out
The audio is copied, not re-encoded. The script sorts the parts so that 2 comes before 10, writes list.txt and chapters.txt beside them, and saves the book as book.m4b in the folder above. chapters.txt is the second input to FFmpeg, and -map_metadata 1 -map_chapters 1 take the book's tags and chapters from it, because the joined audio carries neither. The cover comes from the first part. The $esc line puts a backslash before =, ;, # and \ in titles, which FFmpeg's metadata file otherwise reads as syntax. If book.m4b is already there, FFmpeg asks before overwriting it; answer y, or move the old one first.
Check the result:
ffprobe -v error -show_chapters -of compact "..\book.m4b"
With three parts of two chapters each, the book had six chapters. The first chapter of the second part started where the first part ended, at 12.37 seconds. The title, author and album were in the file, and so was the cover.
The chapter marks drift slightly. The audio of each later part started a little after its chapter mark, about 0.03 seconds late for the second part and 0.05 seconds for the third in testing. The gap grew from the second part to the third, so a book of dozens of parts may be off by more. A part's own chapters can also be wrong to begin with: if the book jumps to the wrong place, rewrite the chapter list in the finished file.
When the parts don't match
Concat copy needs every part to have the same codec, sample rate and number of channels. List them:
Get-ChildItem *.m4b | ForEach-Object { "$($_.Name): " + (ffprobe -v error -select_streams a:0 -show_entries stream=codec_name,profile,sample_rate,channels -of csv=p=0 $_.Name) }
Each line reads like part1.m4b: aac,LC,44100,2: codec, AAC profile, sample rate and channels. If the lines differ, don't copy the parts. In testing, with one part at 22,050 Hz mono and the others at 44,100 Hz stereo, FFmpeg joined them without any error. Decoding the result then printed hundreds of errors, the middle part played as noise and the decoded audio came out about nine seconds shorter than the parts together. Adding a re-encode to the same command did not help: the middle part still came out as noise. Only a different sample rate and channel count were tested; a different codec or AAC profile is the same kind of mismatch, but it was not tested. Voxbound converts parts that don't match for you: join this book free in Voxbound.
The fix is to convert every part to the same settings first, once each, and then join the converted parts. This writes them to a folder called fixed next to the current one, with the chapters and cover kept:
New-Item -ItemType Directory -Force ..\fixed | Out-Null
Get-ChildItem *.m4b | ForEach-Object { ffmpeg -v error -i $_.Name -map 0:a -map 0:v? -ar 44100 -ac 2 -c:a aac -b:a 128k -c:v copy -disposition:v attached_pic "..\fixed\$($_.Name)" }
Change 44100, 2 and 128k to the sample rate, channel count and bitrate you want; use at least the bitrate of your best part. Then open PowerShell in fixed and run the script from the last section there. In testing, the joined book then had the 440 Hz, 550 Hz and 660 Hz tones of the three parts in order, with six chapters.
With Voxbound
Voxbound joins the files without commands, and converts parts that don't match for you:
- Drag the folder with the parts onto the Voxbound window, or click Choose folder. Files are put in natural order, so
2comes before10. Drag rows to reorder them. - Check the title and author in the Book card. Click Import tags to copy them from the first file, or Look up to find them online. A cover inside the first file is used automatically.
- To check or rename the chapters, open the Chapters tab and click Auto-generate from files.
- Click Merge files.
The chapters inside the parts aren't kept. You get one chapter per file, named from the file name, which you can rename, or you can import a chapter list; the formats page shows the list format. Voxbound 4.5.1 was run from the command line on the same test parts. With the parts at the same settings and Smart mode on, the audio was copied without re-encoding, and the bitrate stayed at the parts' 64 kbps. With Smart mode off, it was re-encoded at the default 128k. Either way the book had three chapters, named after the three files, at 0, 12.37 and 21.58 seconds, plus the cover, and the title came from the folder name unless one was given. With the 22,050 Hz mono part in the middle, Voxbound converted all three to one format, as its Source file warnings dialog says it will, and the three tones came out in order. The conditions for a copy are listed on the formats page. The free trial covers one book; see the download page.
Which way to use
| Way | Audio | Chapters | Tags and cover | Parts that don't match |
|---|---|---|---|---|
| FFmpeg, plain join | Copied | None | No tags; the cover from the first part, with one option | Joined without an error, and damaged |
| FFmpeg, the script | Copied | Each part's chapters, or one for a part without any | The title and author you set; the cover from the first part | Convert them first, then join |
| Voxbound | Copied when the files match and Smart mode can copy; otherwise re-encoded | One per file, which you can rename, or an imported list | From the first file with Import tags, or online with Look up; the cover from the files | Converted to one format for you |
If you only need a different file type, such as one MP3, see converting M4B to MP3. Our guide to choosing an audiobook merger compares the two ways on more points.
Questions
Short answers to what people ask most about merging M4B files.
How do I merge M4B files into one?
Join them with FFmpeg's concat option, which copies the audio without re-encoding, and give the result a chapter list and tags, because a plain join keeps neither. The script on this page does that. In Voxbound, drop the files in, check their order and click Merge files.
Can I combine M4B files without losing the chapters?
With FFmpeg, yes, if you write the chapters back: the script on this page moves each part's chapters to where that part starts in the joined book. Voxbound doesn't keep the chapters inside the files; it makes one chapter per file, which you can rename, or you can import a chapter list.
Does merging M4B files re-encode them?
Not with the FFmpeg commands on this page, which copy the audio. Voxbound also copies it when the files match and Smart mode is on, and otherwise re-encodes it at the Quality you chose.
Why does my merged M4B have no chapters?
A plain join doesn't carry chapters over. In testing, FFmpeg's concat option with copy kept none. Write them back with the script on this page, or add them to the finished file.
Can I merge M4B files that have different sample rates?
Not by copying them. In testing, FFmpeg joined parts with different sample rates and channel counts without an error, and the middle part came out as noise. Convert every part to the same settings first, as this page shows. Voxbound converts them for you.
Can I merge M4B files for free?
Yes, with FFmpeg, which is free: the script on this page needs nothing else. Voxbound makes one complete book free before you pay.
Rather skip the commands?
Voxbound joins M4B, M4A and MP3 files into one M4B with chapters, a cover and the right settings for your player.
Try one book free Buy Voxbound