13

I have many (7500) .wav files (named by their numbers) which I'd like to merge into one. I think it has to be done with a bash loop because with using globbing some files would be in the wrong order (like 1111 before 2).

What's the best way to do this under Linux?

killermist
  • 1,949
Neula
  • 1,033

2 Answers2

21

If all the files have the same parameters, like sample rate and number of channels, you still can't just catenate them. You have to strip away the WAV header.

It may be easiest to use an audio file manipulation utility like sox, which contains a method for catenating files. In fact, it does this by default. E.g. to combine three .wav files into one long one:

$ sox short1.wav short2.wav short3.wav long.wav

A loop will not arrange the files in the order you want. What you want is to sort the names, but treat them numerically. sort -n will do this.

Thus:

$ sox $(ls *.wav | sort -n) out.wav

If sox cannot handle that many files, we can break up the job like this:

$ ls *.wav | sort -n > script

Then we have a script file which looks like:

1.wav
2.wav
...
3999.wav
4000.wav
...
file7500.wav

We edit that to make several command lines:

# catenate about half the files to temp1.wav   
sox \
1.wav \
2.wav \
... \
3999.wav \
temp1.wav

# catenate the remainder to temp2.wav  
sox \
4000.wav \
... \
7500.wav \
temp2.wav

# catenate the two halves
sox temp1.wav temp2.wav out.wav ; rm temp1.wav temp2.wav

As the first editing step on the file list, you can use the vi command :%s/.*/& \\/ to add a backslash after every line.

Kaz
  • 2,800
  • 1
  • 20
  • 24
0

This has been bugging me today as well as I had many more files to concatenate than sox can load. So I ended up using this shell script inside the folder with the wavs (please make sure you don't need in.wav and out.wav as they'll be overwritten):

rm in.wav out.wav                                                                                                                            
ls *.wav | sort -n | while read l;
do                                                                                                                                                            
   if [ ! -f in.wav ]                                                                                                                                         
   then                                                                                                                                                       
      cp $l in.wav                                                                                                                                            
   else                                                                                                                                                       
      sox in.wav $l out.wav                                                                                                                                   
      cp out.wav in.wav                                                                                                                                       
   fi                                                                                                                                                         
   echo "$l"                                                                                                                                                  
done        
``