Behringer X-Live – Splitting 32 Channel WAV Files and Deleting Silence

At my church we use a Behringer X32 mixer to run Sunday services and we added an X-Live card, instead of the included USB Audio interface card, so we can record our services, multi-tracked, direct to an SD card. This has saved a lot of recording overhead, since we don’t need a PC, display, etc., but it’s also created some hassles.

The X-Live card works great, but it does lack some flexibility we’d like. For instance, you can record 8/16/32 channels off the board but you can’t really pick which ones. For the mix of channels we need, I tell the X-Live to just capture all 32 channels. This works great, but it doesn’t record 32 individual files; It records all 32 channels into one multi-channel file that are a bit over 12 minutes in length. From a SD Card bandwidth standpoint, it’s much easier to write one big file than it is to write 32 separate files. When the file hits the 4GB limit of the SD Card’s filesystem, the X-Live closes that file, and opens a new one seamlessly. There is not gap, pop, or pause between the two files.

When it comes time to work with the files though, you have to do some cleanup before you can really get started. Your DAW, like Pro Tools, Reaper, Cubase, etc., will easily open the files the X-Live creates, but there is no clean way to import this one multi-track file as 32 channels in your DAW. It creates one channel as though the file were a really massive surround sound mix.

For a while I was opening the files in (the excellent, free and open source audio editor) Audacity, removing the silent ones by hand and then exporting out just the tracks I need. Its tedious, and I would often get behind in the clean up, which means I was using up a lot of storage. For just the music from a two service Sunday I was looking at around 30GB of data.

There had to be a better way…

The first step was to use a tool to break the 32 channel file into 32 separate files. I had tried to do this once before with ffmpeg. I knew it was possible, but I hadn’t been able to get it working the way I wanted. I lost invested 5 or 6 hours this past weekend to try again and finally had some more success.

I first started with the ffprobe command to give me the details of the file.

ffprobe (filename)

For my use, it was really only the very last line of it’s output I needed to see:

Stream #0:0: Audio: pcm_s32le ([1][0][0][0] / 0x0001), 44100 Hz, 32 channels, s32, 45158 kb/s

The important details are the name of the stream, #0.0. The audio codec, pcm_s32le. The sample rate, 44.1k (We run our X32 at the 44.1k sample rate, for easier playback from USB thumbdrives, but you may have yours setup for 48k.). And finally, the number of channels, 32.

We now have the raw information we need to build our ffmpeg command. It’s quite long though, so I created the command as a bash script on Centos 8.

You run the command by calling the script, which I called ‘Split’. It needs you to enter two variables to run, which are noted in the script by $1, and $2. The first input needed is the filename you want to split, and after that you specify the file name prefix of the output. I usually have 2 or more files to split from the same recording and I wanted to be able to split them into the same folder without risk of overwriting any of the files.


#!/bin/bash
#
# To run, use script name, then the file nane, and then the prefix for the saved files.
#
# i.e. 'Split 00000001.wav 01'
#
# This will process the file 00000001.wav and create 32 files, 1-32, prefixed by '01-'
#
# 01-01.wav
# 01-02.wav
# 01-03.wav
#
ffmpeg -i $1 -ar 44100 -acodec pcm_s32le -map_channel 0.0.0 $2-01.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.1 $2-02.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.2 $2-03.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.3 $2-04.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.4 $2-05.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.5 $2-06.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.6 $2-07.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.7 $2-08.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.8 $2-09.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.9 $2-10.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.10 $2-11.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.11 $2-12.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.12 $2-13.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.13 $2-14.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.14 $2-15.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.15 $2-16.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.16 $2-17.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.17 $2-18.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.18 $2-19.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.19 $2-20.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.20 $2-21.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.21 $2-22.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.22 $2-23.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.23 $2-24.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.24 $2-25.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.25 $2-26.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.26 $2-27.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.27 $2-28.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.28 $2-29.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.29 $2-30.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.30 $2-31.wav \
-ar 44100 -acodec pcm_s32le -map_channel 0.0.31 $2-32.wav

What I’m doing with the script is using the X32’s wav file and splitting each ‘channel’ into it’s own file.

ffmpeg -i $1 is running the program ffmpeg, and then using as an input (-i) the first variable you entered when you ran the command. From there, we just map each channel to a new file, and we tell ffmpeg what format the new file should be in.

-ar 44100 tells ffmpeg to output 44.1k, just like the source file. You should change this to match your ffprobe output. If you’re X32 is set to 48k, swap this for 48000 instead.

-acodec pcm_s32l3 tells ffmpeg to output 32bit files. I was suprised the X-Live recorded in 32bits, but if you don’t specify anything it will default to 16bits.

-map_channel 0.0.X tells ffmpeg which stream/track of the file to select.

And Finally, $2-XX.wav tells ffmpeg to use the 2nd variable you entered when you ran the command as a prefix to the filename it creates. I also use this file naming process to map wav files to the channels on the board. We don’t have a channel zero, for instance, we have channel one. So I map stream 0 to wav file 1, and so on, for my sanity down the road.

The last detail, the \ at the end of each line, just tells BASH that the command isn’t done and to keep going.

Once you run the script it will create all 32 files simultaneously. On the very low power server I’m running this on, it takes 20-30 minutes. I also tried running 32 ffmpeg commands, instead of 1 command with 32 streams being saved out, and it took about 50% longer. On a faster system, with fast storage, I’m sure you could cut way down on the time. (I just did a test of the same script on my 2019 Macbook Pro and it ran more than 10X faster. The machine I run this on normally is a repurposed Thecus N5550 running Centos 8 on a ZFS RAIDZ2 of 4x 3TB hard disks.)

Now comes the part that you need to be careful with. The next script ‘listens’ to the files and DELETES the ones that are below a certain volume level.

I found another open source bit of software called SoX, which is a processor of audio. It can insert effects, mix files together.. basically anything except what the above ffmpeg script does.

If you run the software in ‘stat’ mode though you can just get it to output some data about the files. I ran the command against a number of files I knew to be as silent as the X32 can make them, and against a number of files with some amount of known signal.

In my experience, none of the files are actually silent in a true digital sense. The X-Live is recording what it sees, and even a channel we’re not using has some self-noise to it before it hits the recorder. The trick is to set a threshold you’re comfortable with. Through some trial and error, I’ve set the script up to consider anything where the max volume on a channel is below 0.0001 as silent. In my testing, for the X32 we use, these are the volumes I see reported back for tracks I know to not be in use:

0.000015
0.000016
0.000047
0.000023
0.000030
0.000013
0.000012
0.000014
0.000018
0.000017
0.000069
0.000028

For tracks I know to have audio of various levels like room mics, vocalists, drums, piano:

0.090938
0.146886
0.035940
0.189697
0.113916

There is a pretty big difference, thank goodness! Based on that data, looking for audio below 0.0001 felt like a safe limit and that is the variable I inserted in the script. By all means, look for and set your own safe value.

For the script itself, I was lucky enough to find this blog post by David Hilowitz. His goal is the same as mine, deleting silent WAV files, but his execution is a bit different. I used his script as my starting point. I changed variable names, and I just delete the data I don’t want where he, wisely, makes a list of the blank files and listens through them.

David – If you’re still out there. Thanks! You wrote you post many years ago, but it still works great. The only tweak needed to get it running, as some of the commenters mentioned, is the need for to ‘//’ before the g in the sed command.

For the script below, which I call ‘Delete_Silent’ on my system, I have commented out the line to delete audio, and replaced it with a line to echo the file name to the terminal. I used this for testing, until I was happy, and now I have commented out the ‘echo’ line and use the rm line. The output of the script now just reports to me the files it HAS DELETED. Be careful!

The script will look at, and process, all of the .wav files in a folder. It runs the file through SoX to get a ‘Maximum Amplitude’ value. It cleans up the output from SoX to just produce a number and then passes that number through a greater than/equalto routine. If the volume returned is lower than the variable you set, MAX, it deletes it and shows the name of the deleted file.


#!/bin/bash
#Check for Silent, or near silent WAV files.
#
# The process will run on all WAV files inthe
#

MAX=0.0001

for wav in *.wav
do
V=$(sox "$wav" -n stat 2>&1 | grep "Maximum amplitude" | cut -d ":" -f 2 | sed 's/ //g')

if [[ $(echo "if ("$V" > "$MAX") 1 else 0" | bc) -eq 0 ]]
then echo "I think $wav is silent";
# then rm -rfv $wav;
fi
done

There you have it. You can now take those massive X-Live Wav files and pair them down to just the data you need. So much disk space saved!

The next step for me is to have a process look for new files in a folder and process and organize them automatically. But, that’s a post for another day…

Topslakr

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.