2

I have multiple USB audio interfaces that each have 10, 18 or even 32 input channels. Mainly used to record every instrument of a band into a separate track.

I record in raw wav format (s32le @48kHz) which means I need crazy amounts of storage space if I record all channels. For that reason I need to only record the channels that I actually want to record. I found this to be possible using SoX by specifying the amount of channels I need with the -c flag and using the remix "effect" to select the channels to be recorded.

And this little proof of concept shows me that it does work:

$ export SOURCE_NAME="alsa_input.usb-Behringer_FLOW_8_03-FF-02-11-55-44-00.Direct__hw_F8__source"

# Record only 1 channel(s) (-c 1) - The channel(s) to record: 2
$ sox -t pulseaudio "${SOURCE_NAME}" -r 48000 -c 1 -b 16 -e signed-integer output.w64 remix 2

Scaling it up however, doesn't work:

# Record only 4 channel(s) (-c 4) - The channel(s) to record: 1 2 6 8
$ sox -t pulseaudio "${SOURCE_NAME}" -r 48000 -c 4 -b 32 -e signed-integer output.w64 remix 1 2 6 8

For some reason SoX only recognizes the first two channels:

Input File     : 'alsa_input.usb-Behringer_FLOW_8_03-FF-02-11-55-44-00.Direct__hw_F8__source' (pulseaudio)
Channels       : 2
Sample Rate    : 48000
Precision      : 16-bit
Sample Encoding: 16-bit Signed Integer PCM

sox FAIL remix: too few input channels

FFmpeg also fails when recording more than 2 channels:

$ ffmpeg -f pulse -i "${SOURCE_NAME}" -c:a pcm_s32le -ar 48000 -ac 10 -channel_layout 0x3ff output.w64

FFmpeg throws this error:

Guessed Channel Layout for Input Stream #0.0 : stereo
Input #0, pulse, from 'alsa_input.usb-Behringer_FLOW_8_03-FF-02-11-55-44-00.Direct__hw_F8__source':
  Duration: N/A, start: 1689504465.730127, bitrate: 1536 kb/s
  Stream #0:0: Audio: pcm_s16le, 48000 Hz, stereo, s16, 1536 kb/s
Multiple -ac options specified for stream 0, only the last option '-ac 10' will be used.
Stream mapping:
  Stream #0:0 -> #0:0 (pcm_s16le (native) -> pcm_s32le (native))
Press [q] to stop, [?] for help
[pcm_s32le @ 0x55bc4d7d6040] Channel layout '10 channels (FL+FR+FC+LFE+BL+BR+FLC+FRC+BC+SL)' with 10 channels does not match number of specified channels 2
Error initializing output stream 0:0 -- Error while opening encoder for output stream #0:0 - maybe incorrect parameters such as bit_rate, rate, width or height
Conversion failed!

Double checking with ffmpeg probe:

$ ffprobe -f pulse -i "${SOURCE_NAME}"
Input #0, pulse, from 'alsa_input.usb-Behringer_FLOW_8_03-FF-02-11-55-44-00.Direct__hw_F8__source':
  Duration: N/A, start: 1689504633.181940, bitrate: 1536 kb/s
  Stream #0:0: Audio: pcm_s16le, 48000 Hz, 2 channels, s16, 1536 kb/s

So my next thought was that PulseAudio itself has a bug.But we can easily check for that using the pactl utility:

$ pactl list sources
Source #1414
    ...
    Name: alsa_input.usb-Behringer_FLOW_8_03-FF-02-11-55-44-00.Direct__hw_F8__source
    ...
    Sample Specification: s32le 10ch 48000Hz
    Channel Map: aux0,aux1,aux2,aux3,aux4,aux5,aux6,aux7,aux8,aux9
    ...
    Volume: aux0: 48287 /  74% / -7.96 dB,   aux1: 48287 /  74% / -7.96 dB,   aux2: 48287 /  74% / -7.96 dB,   aux3: 48287 /  74% / -7.96 dB,   aux4: 48287 /  74% / -7.96 dB,   aux5: 48287 /  74% / -7.96 dB,   aux6: 48287 /  74% / -7.96 dB,   aux7: 48287 /  74% / -7.96 dB,   aux8: 48287 /  74% / -7.96 dB,   aux9: 48287 /  74% / -7.96 dB
            balance 0.00
    ...
    Properties:
        ...
        audio.channels = "10"
        ...

This makes it quite obvious that PulseAudio is aware of all 10 input channels of that USB audio interface.

So I tried using PulseAudio's tool parecord:

$ parecord --device=${SOURCE_NAME} --format=s32le --rate=48000 --channels 10 --file-format=w64 output.w64
Warning: failed to write channel map to file.

and although it produced this warning (whatever it means), it did actually record all 10 channels successfully.

I was even able to select specific channels like this:

parecord --device=${SOURCE_NAME} --format=s32le --rate=48000 --channels 4 --channel-map=aux0,aux1,aux5,aux7 --file-format=w64 output.w64

So why is this not working with SoX or FFmpeg?

I also tried telling SoX to use ALSA instead, but that doesn't work at all:

$ sox -t alsa "plughw:CARD=F8,DEV=0" -r 48000 -c 4 -b 32 -e signed-integer output.w64 remix 1 2 6 8
sox FAIL formats: can't open input  `plughw:CARD=F8,DEV=0': snd_pcm_open error: Device or resource busy

I guess access via ALSA just doesn't work when you have PipeWire and PulseAudio running on top of it.

I checked if I can record via ALSA's arecord utility, but I get the same "device busy" error:

$ arecord -D plughw:CARD=F8,DEV=0 -r 48000 -c 10 -f S32_LE -t wav output.wav
arecord: main:867: audio open error: Device or resource busy

Directly recording using PipeWire's pw-record utility worked just fine though btw:

$ pw-record --target ${SOURCE_NAME} --format s32 --rate 48000 --channels 10

And I was also able to select the channels I want to record:

$ pw-record --target ${SOURCE_NAME} --format s32 --rate 48000 --channels 4 --channel-map=aux0,aux1,aux5,aux7 output.w64

I looked into SoX and if it supports PipeWire directly, but that doesn't appear to be the case unfortunately. But since PulseAudio does actually see all channels, I don't understand why SoX and FFmpeg are failing here

Any ideas?

Forivin
  • 779
  • 5
  • 18
  • 37
  • Can't you put `-c 10` after `-t pulseaudio` to define the input? – meuh Jul 16 '23 at 13:14
  • It doesn't make a difference if I specify `-c 4` or `-c 10`. I still get `sox FAIL remix: too few input channels`. – Forivin Jul 16 '23 at 14:52
  • I meant a `-c 10` just before the input file, and a `-c 4` just before the output file. – meuh Jul 16 '23 at 14:54
  • Like this? `sox -t pulseaudio -c 10 "${SOURCE_NAME}" -r 48000 -b 32 -e signed-integer -c 4 output.w64 remix 1 2 6 8`? ... In that case I get the error `sox FAIL formats: can't open input \`alsa_input.usb-Behringer_FLOW_8_03-FF-02-11-55-44-00.Direct__hw_F8__source': can not open audio device: Invalid argument`. It only works when I specify `-c 2` for both input and output, but again, then it only records the first two channels... – Forivin Jul 16 '23 at 15:55
  • sox is fussy about the order of args. To avoid problems always put all the options that apply to input before naming the input, and same for output. eg: `sox -t pulseaudio -c 10 -r 48000 -b 32 -e signed-integer "${SOURCE_NAME}" -c 4 output.w64 remix 1 2 6 8` – meuh Jul 16 '23 at 17:18
  • Okay, I understand what you mean, but it's still not working. I get this error for your suggested command: `sox FAIL formats: can't open input \`alsa_input.usb-Behringer_FLOW_8_03-FF-02-11-55-44-00.Direct__hw_F8__source': can not open audio device: Invalid argument`. If i reduce the input channels and output channels to 2 by specifying `-c 2` on both sides, it works, but then it only records the first two channels... – Forivin Jul 19 '23 at 09:40
  • I don't know more about why this doesn't work, but since `parecord` works you could perhaps use it faily minimally and pipe its output into sox if you want to manipulate the stream further. – meuh Jul 19 '23 at 13:37
  • Yes, that is possible and I've already tried it successfully, but it's not what I'm looking for. I would like to use just SoX for more control, reliability, performance and portability. – Forivin Jul 19 '23 at 16:05

0 Answers0