The best — though least helpful — way to do this is not to use MP3 files as your source files. WAV, FLAC or M4A files don't have this problem.
MP3s aren't made up of fixed-rate samples, so cropping out a section of an arbitrary length will not work as you expect. Unless the encoder was smart (like lame), there will often be a gap at the start or end of the MP3 file's audio. I did a test with a sample 0.98s long (which is precisely 73½ CDDA frames, and many MP3 encoders use frames for minimum sample lengths). I then encoded the sample with three different MP3 encoders (lame, sox, and the ancient shine), then decoded those files with three decoders (lame, sox, and madplay). Here's how the sample lengths compare to the original:
Enc.→Dec. Length Samples CDDA Frames
----------------- --------- ------- -----------
shine→lame 0.95" 42095 71.5901
shine→madplay 0.97" 42624 72.4898
shine→sox 0.97" 42624 72.4898
lame→lame 0.98" 43218 73.5000
*Original 0.98" 43218 73.5000
sox→sox 0.99" 43776 74.4490
sox→lame 1.01" 44399 75.5085
lame→madplay 1.02" 44928 76.4082
lame→sox 1.02" 44928 76.4082
sox→madplay 1.02" 44928 76.4082
Only the file encoded and decoded by lame ended up the same length (mostly because lame inserts a length tag to correct for these too-short samples, and knows how to decode it). Everything encoded by sox ended up with a tiny gap, no matter what decoder I used. So joining the files will result in tiny clicks.
Your browser is likely mixing and overlapping the source files very slightly so you don't hear the clicks. Gapless playback is hard to do correctly.
Answer from scruss on Stack OverflowThe best — though least helpful — way to do this is not to use MP3 files as your source files. WAV, FLAC or M4A files don't have this problem.
MP3s aren't made up of fixed-rate samples, so cropping out a section of an arbitrary length will not work as you expect. Unless the encoder was smart (like lame), there will often be a gap at the start or end of the MP3 file's audio. I did a test with a sample 0.98s long (which is precisely 73½ CDDA frames, and many MP3 encoders use frames for minimum sample lengths). I then encoded the sample with three different MP3 encoders (lame, sox, and the ancient shine), then decoded those files with three decoders (lame, sox, and madplay). Here's how the sample lengths compare to the original:
Enc.→Dec. Length Samples CDDA Frames
----------------- --------- ------- -----------
shine→lame 0.95" 42095 71.5901
shine→madplay 0.97" 42624 72.4898
shine→sox 0.97" 42624 72.4898
lame→lame 0.98" 43218 73.5000
*Original 0.98" 43218 73.5000
sox→sox 0.99" 43776 74.4490
sox→lame 1.01" 44399 75.5085
lame→madplay 1.02" 44928 76.4082
lame→sox 1.02" 44928 76.4082
sox→madplay 1.02" 44928 76.4082
Only the file encoded and decoded by lame ended up the same length (mostly because lame inserts a length tag to correct for these too-short samples, and knows how to decode it). Everything encoded by sox ended up with a tiny gap, no matter what decoder I used. So joining the files will result in tiny clicks.
Your browser is likely mixing and overlapping the source files very slightly so you don't hear the clicks. Gapless playback is hard to do correctly.
This is my guess for your issue:
- sox does not add time gap during concatenation,
- however it add time-gap in other operations, for instance if you do a conversion before the concatenation.
To find out what happens I suggest you to check all durations of your files at each time (you can use soxi for instance) to see what's going on.
If it doesn't work (the time-gap is added during concatenation), let me please do another guess:
- Sox add time gap because your samples at the beginning or at the end of the file are not close to zero.
To solve this, you could use very short fade-in an fade-out on you files.
Moreover, to force sox to output files with a well-defined length, you could use the trim parameter like this:
sox filein.mp3 trim 0 duration fileout.mp3
You should be able to do something like:
sox short.ogg -p pad 0 6|sox - long.ogg output.ogg
-p option to sox is used for piping - basically, it tells sox to use stdout as the output. Using - as the input to the second sox is actually saying input is stdin (which happens to be the stdout of the previous sox, as we are piping with |). pad 0 6 tells pad 0 seconds at the beginning and 6 seconds at the end.
Hope this helps.
Thanks to icyrock, I managed to find a solution. I'm using:
$ sox short.ogg -p pad 6 0 | sox - -m long.ogg output.ogg
For multi tracks (credits to Orlando):
$ sox starts-last.mp3 -p pad 2 0 | sox - -m starts-second.mp3 -p pad 2 0 | sox - -m starts-first.mp3 combined.mp3
You can do it with one invocation of sox like this:
sox -m in1.mp3 in2.mp3 in3.mp3 out.mp3
If you want to combine this with the pad effect you need to be clearer about what you want.
To combine mix and effects (pad, trim etc) use the following:
sox -m "|sox end.mp3 -p pad 6 0" start.mp3 output.mp3
The general pattern is:
sox -m input1 input2 ... inputN output
where inputX can be either a filename or a pipe in quotes
"|sox end.mp3 -p pad 6"
I found that the simplest solution is to write the command as such:
sox infile outfile start =end
The audio is not sent to the output stream until the start location is reached and specifying an end location can be done by using the "=" sign with the end time.
So the code would now be, for instance:
sox forth.wav 10.wav trim 303.463 =353.790
- Link to the docs http://manpages.ubuntu.com/manpages/precise/man1/sox.1.html
- Relevant excerpt:
trim start [length|=end] The optional length parameter gives the length of audio to output after the start sample and is thus used to trim off the end of the audio. Alternatively, an absolute end location can be given by preceding it with an equals sign. Using a value of 0 for the start parameter will allow trimming off the end only.
No, you have to calculate the length if you want to use sox. You could also use ffmpeg instead which has atrim filter that supports start/end times.