Giter Site home page Giter Site logo

crra / mp3binder Goto Github PK

View Code? Open in Web Editor NEW
45.0 2.0 5.0 486 KB

๐ŸŽต concatenating, joining, binding MP3 files without re-encoding

License: MIT License

Go 100.00%
mp3 mp3-tagger mp3-joiner go-generics audio join concat no-re-encoding fast golang

mp3binder's Introduction

About

mp3binder joins multiple MP31 files into one without re-encoding. It writes ID3v2 tags and chapters2 for each file.

Why?

Older mp3players (e.g. simple stereos for children), are not always capable of playing multiple files in the correct order. Joining them into one file removes this limitation. Or sometimes a single mp3 file is more practical than a directory of files.

How

This tool builds upon Darren Mulholland's wonderfully simple mp3cat with the corresponding low-level library mp3lib which performs all the heavy lifting. The tool adds id3v2 tag support with the help of the id3v2 library by Albert Nigmatzianov.

Features

mp3binder:

  • combines multiple mp3 files without re-encoding
  • can embed a cover image (jpeg, png) to the output file
    • either via the command line option: --cover
    • or by copying from an input file, e.g. the first file: --tcopy 1
    • or automatically if the folder of the mp3 files contain a folder.jpg or cover.jpg file
    • the automation can be disabled with the command line option --nodiscovery
  • can add files between each files (e.g. silence)
    • either via the command line option: --interlace
    • or automatically if the folder of the mp3 files contain a _interlace.mp3 file
    • the automation can be disabled with the command line option --nodiscovery
  • can write chapters based on the id3v2 title of the input files
    • it can be disabled with the command line option: --nochapters
  • can write id3v2 tags to the output file via the command line option: --tapply 'TIT2="My Title",TALB="My album"'

Screenshot

screenshot of the interface

Note: color added for clarity

Usage

mp3builder joins multiple MP3 files into one without re-encoding.
It writes ID3v2 tags and chapters for each file.

Usage:
  mp3builder file1.mp3 file2.mp3 [flags]

Examples:
Calling 'mp3builder' with no parameters in the directory containing the mp3 files
is equivalent to: 'mp3builder .', which is the same as 'mp3builder *.mp3' and
binds all mp3 files to 'foldername.mp3'.

Flags:
      --nodiscovery        no discovery for well-known files (e.g. cover.jpg)
      --nochapters         does not write chapters for bounded files
      --cover string       use image file as artwork
      --verbose            prints verbose information for each processing step
      --force              overwrite an existing output file
      --interlace string   interlace a spacer file (e.g. silence) between each input file
      --output string      output filepath. Defaults to name of the folder of the first file provided
      --input string       file containing a list of input files
      --tapply string      apply id3v2 tags to output file.
                           Takes the format: 'key1="value",key2="value"'.
                           Keys should be from https://id3.org/id3v2.3.0#Declared_ID3v2_frames
      --tcopy int          copy the ID3 metadata tag from the n-th input file, starting with 1
      --lang string        ISO-639 language string used during string manipulation
                           (e.g. uppercasing non-english languages) (default "en-GB")
  -h, --help               help for mp3builder
  -v, --version            version for mp3builder

Examples

Files to be merged can be specified as a list of filenames:

$ mp3binder one.mp3 two.mp3 three.mp3

Alternatively, an entire directory of .mp3 files can be merged:

  • $ mp3binder or $ mp3binder . (the current directory is used)
  • $ mp3binder foo (the files in the 'foo' directory are used)
  • $ mp3binder *.mp3 (the shell expands all mp3 files as individual shell-sorted parameters)

A file containing the names in a custom order:

  • $ mp3binder --input files.txt

ID3 tags can be copied from the n-th input file:

$ mp3binder --tcopy 1 one.mp3 two.mp3 three.mp3

ID3 tags could also be set manually. The name of the tag must be according to https://id3.org/id3v2.3.0#Declared_ID3v2_frames:

  • $ mp3binder . --tcopy 1 --tapply "TRCK=42,TIT2='My sample title'"
  • $ mp3binder . --tapply "TIT2=\"It's like that\""

Please notice the surrounding quotes and ensure proper quoting.

Silence between each tracks via interlace file

Based on: http://activearchives.org/wiki/Padding_an_audio_file_with_silence_using_sox

Create a silence track: sox -n -r 44100 -c 2 silence.mp3 trim 0.0 3.0

If the input material is FBR (fixed bit rate), generate the silence track with the same fixed bit rate using the '-C' option: sox -n -r 44100 -c 2 -C 192 silence.mp3 trim 0.0 3.0. The shell command file one.mp3 gives information about the bit rate of a file.

And apply: mp3bind --interlace silence.mp3 01.mp3 02.mp3

Build instructions

Building is always more complex then just calling build (e.g. adding version information into the binary or naming the binary or optimize the binary by stripping debug information). Instead of a Makefile, a Taskfile.yml is used that holds the instructions for Task. 'Task' is not mandatory but simplifies the workflow. Once installed (instructions), 'Task' provides an executable task that can be called with custom actions.

For example cross compilation for multiple platforms is achieved with task default:build-all, and for the current platform with task default:build or just task. Plain go would be: go build .\cmd\tui\ without build time optimizations and settings (e.g. version information).

The Taskfile.yml gives good hints which commands and options are executed if the developer don't want to use task. In the end 'Task' it's just a simple task runner (collection of commands).

Configure VSCode/Codium

  1. View > Command Palette
  2. Go: choose Go environment
  3. select go1.18

Footnotes

  1. formally: MPEG-1 Audio Layer III or MPEG-2 Audio Layer III โ†ฉ

  2. specification โ†ฉ

mp3binder's People

Contributors

crra avatar dependabot[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

mp3binder's Issues

User error, I am sure

I have Task installed and I ran it in the mp3binder dir. it looks like it was successful, but when I try to run, I get the zsh: command not found.

Task and Go are new to me, so i am sure this is a user error. Any suggestions? Working on a Mac.

user@audiobooks mp3binder-main % task fatal: not a git repository (or any of the parent directories): .git task: [default:test] go test -cover -race ./... task: [default:prepare] mkdir -p dist ? github.com/crra/mp3binder/cmd/tui [no test files] ? github.com/crra/mp3binder/io/rewindingreader [no test files] ? github.com/crra/mp3binder/mp3binder [no test files] ? github.com/crra/mp3binder/mp3binder/tags [no test files] ok github.com/crra/mp3binder/cli (cached) coverage: 68.6% of statements ok github.com/crra/mp3binder/encoding/keyvalue (cached) coverage: 100.0% of statements ok github.com/crra/mp3binder/slice (cached) coverage: 17.9% of statements ok github.com/crra/mp3binder/value (cached) coverage: 100.0% of statements task: [default:build-for] go build -trimpath -ldflags="-w -s -X main.name="mp3binder" -X main.version="" -X main.realm="mp3binder" -extldflags '-static'" -a -buildvcs=false -o dist/darwin_amd64/mp3binder ./cmd/tui

Joining files just halts

Joining a audio book it stops on file 23 of 50 (splitted in chapters). Tried to remove file 23, but then it stops on file 24.

This is strange since this tool has been working for two other audio books of larger size (number of chapters and total output size).

The merged file got warning with ffmpeg

$ go version
go version go1.22.1 linux/amd64

$ ffmpeg -version
ffmpeg version 4.4.2-0ubuntu0.22.04.1 Copyright (c) 2000-2021 the FFmpeg developers

$ git log --oneline -1
2942c1d (grafted, HEAD -> main, origin/main, origin/HEAD) Bump github.com/stretchr/testify from 1.8.2 to 1.8.4

$ ffmpeg -y -f lavfi -i anullsrc=r=48000:cl=stereo -t 0.1 -b:a 192k -ac 2 1.mp3
$ ffmpeg -y -f lavfi -i anullsrc=r=48000:cl=stereo -t 0.1 -b:a 192k -ac 2 2.mp3

$ ffmpeg -i 1.mp3 
Input #0, mp3, from '1.mp3':
  Metadata:
    encoder         : Lavf58.76.100
  Duration: 00:00:00.14, start: 0.023021, bitrate: 226 kb/s
  Stream #0:0: Audio: mp3, 48000 Hz, stereo, fltp, 192 kb/s
    Metadata:
      encoder         : Lavc58.13

$ go run ./cmd/tui --output 3.mp3 1.mp3 2.mp3 
$ ffmpeg -i 3.mp3 
[mp3 @ 0x55837d41e280] Skipping 576 bytes of junk at 426.
Input #0, mp3, from '3.mp3':
  Metadata:
    track           : 1
    encoder         : https://github.com/crra/mp3binder, dev-build
    title           : Mp3binder
  Duration: 00:00:00.37, start: 0.000000, bitrate: 185 kb/s
  Chapters:
    Chapter #0:0: start 0.000000, end 0.168000
      Metadata:
        title           : 1
    Chapter #0:1: start 0.168000, end 0.336000
      Metadata:
        title           : 2
  Stream #0:0: Audio: mp3, 48000 Hz, stereo, fltp, 176 kb/s

I guess the Skipping warning means the XingFrame, right?

mp3 frame head &{Version:1 Layer:3 Protecttion:1 BitrateIndex:5 SampleRateIndex:0 Padding:1 Private:0 Mode:3 ModeExtension:0 Copyright:0 Original:0 Emphasis:0 SampleSize:1152 FrameSize:209}
mp3 bitrate:64000,samplerate:44100,channelcount:2
00000000  ff fb 52 c0 00 00 00 00  00 00 00 00 00 00 00 00  |..R.............|
00000010  00 00 00 00 00 58 69 6e  67 00 00 00 03 00 00 00  |.....Xing.......|
00000020  0e 00 00 1f 80 00 00 00  00 00 00 00 00 00 00 00  |................|
00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000d0  00                                                |.|

Converting issues

Hello
After a file has been converted using
mp3binder --output "01 - Let Me In.mp3" --tcopy 1 --tapply 'TIT2 = "01 - Let Me In"' .
or mp3binder --output "01 - Let Me In" --tcopy 1 --tapply 'TIT2 = "01 - Let Me In"' .
and I play it in Apple Music, I noticed that the time is not measured correctly. it is reading the time of the first file and not the combined files.
So I have loaded the file in VLC and the duration fluctuated and the time is not static. I notice that if i run ffmpeg -err_detect ignore_err -i "01 - Let Me In.mp3" -c copy "01 - Let Me In_fixed.mp3" it corrects the time issue with VLC, and it corrects the file when played in Apple Music.

Not sure if I am doing something wrong. Thank you

Frame went over

Just to report an odd issue I'm seeing when merging some audiobooks with your excellent tool - in verbose mode mp3binder reports:

"frame went over tag area"

and the merge fails at that point. It's only on a few books. Is there any more useful information I can provide?

Tags containing , and "" quotation (like in --help)

The --help of mp3binder for --tapply contains: Takes the format: 'key1="value",key2="value"'.
However, this "" quotation has a bug because of wrong parsing the , split:

The example... mp3binder.exe . --tapply TIT2="Testing,Bug"
... results in "! Warning: the tag 'Bug' is not a well-known tag, but will be written"

Single '' quotation works: mp3binder.exe . --tapply TIT2='Testing,Bug'

This is a workaround, or maybe meant to work this way anyway(?) - unfortunately there's no way to include rather common ' and/or - chars in the tag, or I dunno how to replace/escape them.

The example... mp3binder.exe . --tapply TIT2='Testing,'Workaround'Bug'
... results in the tag just being Testing,WorkaroundBug

Using Windows release 5.1.0.

Edit: A solution would be to be able to priovide a sidecar file for tagging, with a single TAGTYPE=... in each lin so there is no need for command line parsing - like 028bb72

Output file corrupt

Hello @crra

Thank you for making this addition.

There is something about the created output file that Windows Media Player doesn't like, unfortunately. I can play it with VLC though.

Using mpck (https://github.com/Sjord/checkmate) on the built file, it reports the following:

SUMMARY: foo.mp3
    version                       MPEG v1.0
    layer                         3
    average bitrate               -21743 bps (VBR)
    samplerate                    44100 Hz
    frames                        2020051
    time                          -743:-42.-869
    unidentified                  886 b (0%)
    errors                        unidentified bytes
                                  inconsistent frame headers
                                  invalid header values
    result                        Bad

If I use the same tool on the (397) source files, they are all reported OK.

I hope that helps. Please let me know if you need me to run anything else over the files.

Originally posted by @pcolmer in #22 (comment)

Add ability for chapters [feature request]

Would it be possible to add the ability to make chapter markers for merging mp3 files when working with audiobooks? For example, if I have a directory of mp3 files that I'm merging, to make a new chapter marker for every file in the final outputted single mp3 file?

Thanks for the consideration

Doesn't handle filenames with spaces in

I'm trying to use this tool in a way that it processes a list of files from a text file rather than allowing it to glob the directory or specifying the files individually within the command.

For example:

ls *.mp3 > list.txt
FILELIST=$(xargs -d "\n" -a list.txt ls);  mp3binder $FILELIST --lang "en-GB" --output ~/tmp.mp3

The reason for taking this approach is because some of my directories of files don't have them named correctly to respect the play order (e.g. they are missing leading zeroes on the track numbers). So I ls the directory to a file, edit the file to fix the order and then feed the list to mp3binder.

However, if the files have spaces in their names, it doesn't work. If I leave out --lang "en-GB" I get the error provided language 'C': unsupported language. After I explicitly specify --lang, the utility breaks on the first space in the first filename. I then try to work around that by getting ls to put double-quotes around the filename:

FILELIST=$(xargs -d "\n" -a list.txt ls --quoting-style=c); mp3binder $FILELIST --lang "en-GB" --output ~/tmp.mp3

but it still breaks on the first space in the first filename. It doesn't look like the commandline parser is respecting (or looking for) double-quotes.

Unable to compile

Hi, I have spent the better part of the afternoon trying to figure out how to compile this app, but failed to. Would it be possible to add that bit of info to the readme?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.