Telegram voice calls with JACK on FreeBSD 15 and a 24-channel USB interface (Soundcraft Signature 22 MTK)

Environment​

  • OS: FreeBSD 15
  • Audio server: JACK (OSS backend)
  • USB audio interface: Soundcraft Signature 22 MTK
  • The mixer exposes 24 USB capture channels.
The mixer is used as the primary audio interface, and all audio routing on the system is done through JACK.

The USB device therefore appears in JACK as:

Code:
system:capture_1
system:capture_2
...
system:capture_24

Signal path​

Code:
microphone
   ↓
external processing (mic preamp / dbx processor)
   ↓
Soundcraft Signature 22 MTK
   ↓
USB multichannel audio (24 channels)
   ↓
JACK
   ↓
applications (OBS, Telegram, etc.)


Important channel mapping​

In this setup:

  • system:capture_1 = microphone signal
  • system:playback_21 and system:playback_22 = USB return channels to the mixer



Application audio is returned to the mixer through PulseAudio:

Code:
PulseAudio JACK Sink:front-left  → system:playback_21
PulseAudio JACK Sink:front-right → system:playback_22


Observed problems​

1. Telegram binary package​

The Telegram package available for FreeBSD does not work correctly in this setup.

Telegram appears to expect a mono microphone device.

However the audio device exposed to applications has 24 capture channels, and Telegram does not correctly select the microphone channel.

2. PulseAudio JACK bridge​

PulseAudio is currently used as a bridge between some applications and JACK.

The PulseAudio JACK source inherits the full number of channels from JACK:

  • 24-channel input
  • 22-channel output
As a result, Telegram sees a multichannel microphone device, not a mono microphone.

3. Attempt to rebuild Telegram with JACK support​

An attempt was made to rebuild Telegram on FreeBSD so that it could use OpenAL Soft (alsoft) with JACK support, allowing Telegram to talk directly to JACK.

This build attempt failed and Telegram could not be successfully compiled with this configuration.

What I need​

Conceptually I need something like this:

Code:
224-channel USB interface
        ↓
JACK routing
        ↓
virtual mono microphone device
        ↓
Telegram
In other words:

  • the microphone signal is system:capture_1
  • Telegram should receive only that channel as a mono input device



JACK routing​

Output of:


jack_lsp -c


Code:
system:capture_1
   LSP Graphic Equalizer x32 Mono:in
   OBS Studio: mic:in_1
system:capture_2
system:capture_3
system:capture_4
system:capture_5
system:capture_6
system:capture_7
system:capture_8
system:capture_9
system:capture_10
system:capture_11
system:capture_12
system:capture_13
system:capture_14
system:capture_15
   OBS Studio: solo:in_1
system:capture_16
   OBS Studio: solo:in_2
system:capture_17
system:capture_18
system:capture_19
system:capture_20
system:capture_21
system:capture_22
system:capture_23
system:capture_24
system:playback_1
system:playback_2
system:playback_3
system:playback_4
system:playback_5
system:playback_6
system:playback_7
system:playback_8
system:playback_9
system:playback_10
system:playback_11
system:playback_12
system:playback_13
system:playback_14
system:playback_15
system:playback_16
system:playback_17
system:playback_18
system:playback_19
system:playback_20
system:playback_21
   PulseAudio JACK Sink:front-left
system:playback_22
   PulseAudio JACK Sink:front-right
PulseAudio JACK Sink:front-left
   system:playback_21
   OBS Studio: pa:in_1
PulseAudio JACK Sink:front-right
   system:playback_22
   OBS Studio: pa:in_2
PulseAudio JACK Sink:rear-left
PulseAudio JACK Sink:rear-right
PulseAudio JACK Sink:front-center
PulseAudio JACK Sink:lfe
PulseAudio JACK Sink:side-left
PulseAudio JACK Sink:side-right
PulseAudio JACK Sink:aux0
PulseAudio JACK Sink:aux1
PulseAudio JACK Sink:aux2
PulseAudio JACK Sink:aux3
PulseAudio JACK Sink:aux4
PulseAudio JACK Sink:aux5
PulseAudio JACK Sink:aux6
PulseAudio JACK Sink:aux7
PulseAudio JACK Sink:aux8
PulseAudio JACK Sink:aux9
PulseAudio JACK Sink:aux10
PulseAudio JACK Sink:aux11
PulseAudio JACK Sink:aux12
PulseAudio JACK Sink:aux13
PulseAudio JACK Source:front-left
PulseAudio JACK Source:front-right
PulseAudio JACK Source:rear-left
PulseAudio JACK Source:rear-right
PulseAudio JACK Source:front-center
PulseAudio JACK Source:lfe
PulseAudio JACK Source:side-left
PulseAudio JACK Source:side-right
PulseAudio JACK Source:aux0
PulseAudio JACK Source:aux1
PulseAudio JACK Source:aux2
PulseAudio JACK Source:aux3
PulseAudio JACK Source:aux4
PulseAudio JACK Source:aux5
PulseAudio JACK Source:aux6
PulseAudio JACK Source:aux7
PulseAudio JACK Source:aux8
PulseAudio JACK Source:aux9
PulseAudio JACK Source:aux10
PulseAudio JACK Source:aux11
PulseAudio JACK Source:aux12
PulseAudio JACK Source:aux13
PulseAudio JACK Source:aux14
PulseAudio JACK Source:aux15
LSP Graphic Equalizer x32 Mono:in
   system:capture_1
LSP Graphic Equalizer x32 Mono:out
OBS Studio: mic:in_1
   system:capture_1
OBS Studio: solo:in_1
   system:capture_15
OBS Studio: solo:in_2
   system:capture_16
OBS Studio: pa:in_1
   PulseAudio JACK Sink:front-left
OBS Studio: pa:in_2
   PulseAudio JACK Sink:front-right

PulseAudio configuration​


pactl list short sinks


Code:
1  jack_out  module-jack-sink.c  float32le 22ch 48000Hz  IDLE


pactl list short sources


Code:
1 1  jack_out.monitor  module-jack-sink.c  float32le 22ch 48000Hz  IDLE<br>2  jack_in           module-jack-source.c float32le 24ch 48000Hz  IDLE<br>

Detailed PulseAudio sink information​


pacmd list-sinks



Code:
1 sink(s) available.
  * index: 1
    name: <jack_out>
    driver: <module-jack-sink.c>
    flags: DECIBEL_VOLUME LATENCY
    state: IDLE
    suspend cause: (none)
    priority: 10000
    volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB,   rear-left: 65536 / 100% / 0.00 dB,   rear-right: 65536 / 100% / 0.00 dB,   front-center: 65536 / 100% / 0.00 dB,   lfe: 65536 / 100% / 0.00 dB,   side-left: 65536 / 100% / 0.00 dB,   side-right: 65536 / 100% / 0.00 dB,   aux0: 65536 / 100% / 0.00 dB,   aux1: 65536 / 100% / 0.00 dB,   aux2: 65536 / 100% / 0.00 dB,   aux3: 65536 / 100% / 0.00 dB,   aux4: 65536 / 100% / 0.00 dB,   aux5: 65536 / 100% / 0.00 dB,   aux6: 65536 / 100% / 0.00 dB,   aux7: 65536 / 100% / 0.00 dB,   aux8: 65536 / 100% / 0.00 dB,   aux9: 65536 / 100% / 0.00 dB,   aux10: 65536 / 100% / 0.00 dB,   aux11: 65536 / 100% / 0.00 dB,   aux12: 65536 / 100% / 0.00 dB,   aux13: 65536 / 100% / 0.00 dB
            balance 0.00
    base volume: 65536 / 100% / 0.00 dB
    volume steps: 65537
    muted: no
    current latency: 16.71 ms
    max request: 22 KiB
    max rewind: 0 KiB
    monitor source: 1
    sample spec: float32le 22ch 48000Hz
    channel map: front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right,aux0,aux1,aux2,aux3,aux4,aux5,aux6,aux7,aux8,aux9,aux10,aux11,aux12,aux13
    used by: 0
    linked by: 0
    fixed latency: 13.33 ms
    module: 17
    properties:
        device.api = "jack"
        device.description = "JACK sink (PulseAudio JACK Sink)"
        jack.client_name = "PulseAudio JACK Sink"
        device.icon_name = "audio-card"

Detailed PulseAudio source information​

pacmd list-sources


Code:
2 source(s) available.
    index: 1
    name: <jack_out.monitor>
    driver: <module-jack-sink.c>
    flags: DECIBEL_VOLUME LATENCY
    state: IDLE
    suspend cause: (none)
    priority: 1000
    volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB,   rear-left: 65536 / 100% / 0.00 dB,   rear-right: 65536 / 100% / 0.00 dB,   front-center: 65536 / 100% / 0.00 dB,   lfe: 65536 / 100% / 0.00 dB,   side-left: 65536 / 100% / 0.00 dB,   side-right: 65536 / 100% / 0.00 dB,   aux0: 65536 / 100% / 0.00 dB,   aux1: 65536 / 100% / 0.00 dB,   aux2: 65536 / 100% / 0.00 dB,   aux3: 65536 / 100% / 0.00 dB,   aux4: 65536 / 100% / 0.00 dB,   aux5: 65536 / 100% / 0.00 dB,   aux6: 65536 / 100% / 0.00 dB,   aux7: 65536 / 100% / 0.00 dB,   aux8: 65536 / 100% / 0.00 dB,   aux9: 65536 / 100% / 0.00 dB,   aux10: 65536 / 100% / 0.00 dB,   aux11: 65536 / 100% / 0.00 dB,   aux12: 65536 / 100% / 0.00 dB,   aux13: 65536 / 100% / 0.00 dB
            balance 0.00
    base volume: 65536 / 100% / 0.00 dB
    volume steps: 65537
    muted: no
    current latency: 0.00 ms
    max rewind: 0 KiB
    sample spec: float32le 22ch 48000Hz
    channel map: front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right,aux0,aux1,aux2,aux3,aux4,aux5,aux6,aux7,aux8,aux9,aux10,aux11,aux12,aux13
    used by: 0
    linked by: 0
    fixed latency: 13.33 ms
    monitor_of: 1
    module: 17
    properties:
        device.description = "Monitor of JACK sink (PulseAudio JACK Sink)"
        device.class = "monitor"
        device.icon_name = "audio-input-microphone"
  * index: 2
    name: <jack_in>
    driver: <module-jack-source.c>
    flags: DECIBEL_VOLUME LATENCY
    state: IDLE
    suspend cause: (none)
    priority: 10000
    volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB,   rear-left: 65536 / 100% / 0.00 dB,   rear-right: 65536 / 100% / 0.00 dB,   front-center: 65536 / 100% / 0.00 dB,   lfe: 65536 / 100% / 0.00 dB,   side-left: 65536 / 100% / 0.00 dB,   side-right: 65536 / 100% / 0.00 dB,   aux0: 65536 / 100% / 0.00 dB,   aux1: 65536 / 100% / 0.00 dB,   aux2: 65536 / 100% / 0.00 dB,   aux3: 65536 / 100% / 0.00 dB,   aux4: 65536 / 100% / 0.00 dB,   aux5: 65536 / 100% / 0.00 dB,   aux6: 65536 / 100% / 0.00 dB,   aux7: 65536 / 100% / 0.00 dB,   aux8: 65536 / 100% / 0.00 dB,   aux9: 65536 / 100% / 0.00 dB,   aux10: 65536 / 100% / 0.00 dB,   aux11: 65536 / 100% / 0.00 dB,   aux12: 65536 / 100% / 0.00 dB,   aux13: 65536 / 100% / 0.00 dB,   aux14: 65536 / 100% / 0.00 dB,   aux15: 65536 / 100% / 0.00 dB
            balance 0.00
    base volume: 65536 / 100% / 0.00 dB
    volume steps: 65537
    muted: no
    current latency: 4.98 ms
    max rewind: 0 KiB
    sample spec: float32le 24ch 48000Hz
    channel map: front-left,front-right,rear-left,rear-right,front-center,lfe,side-left,side-right,aux0,aux1,aux2,aux3,aux4,aux5,aux6,aux7,aux8,aux9,aux10,aux11,aux12,aux13,aux14,aux15
    used by: 0
    linked by: 0
    fixed latency: 2.67 ms
    module: 18
    properties:
        device.api = "jack"
        device.description = "JACK source (PulseAudio JACK Source)"
        jack.client_name = "PulseAudio JACK Source"
        device.icon_name = "audio-input-microphone"

JACK configuration​

Driver parameters:

jack_control dp



Code:
--- get driver parameters (type:isset:default:value)
                rate: Sample rate (uint:set:48000:48000)
              period: Frames per period (uint:set:1024:256)
            nperiods: Number of periods to prefill output buffer (uint:notset:1:1)
          wordlength: Word length (sint:set:16:32)
          inchannels: Capture channels (uint:notset:2:2)
         outchannels: Playback channels (uint:notset:2:2)
                excl: Exclusive and direct device access (bool:set:False:True)
             capture: Input device (str:notset:/dev/dsp:/dev/dsp)
            playback: Output device (str:notset:/dev/dsp:/dev/dsp)
              device: OSS device name (str:set:/dev/dsp:/dev/dsp4)
         ignorehwbuf: Ignore hardware period size (bool:notset:False:False)
       input-latency: Extra input latency (uint:notset:0:0)
      output-latency: Extra output latency (uint:notset:0:0)



Available drivers:

jack_control dl



Code:
--- drivers list
net
loopback
netone
dummy
proxy
oss


Engine parameters:

jack_control ep



Code:
--- get engine parameters (type:isset:default:value)
              driver: Driver to use (str:set:dummy:oss)
                name: Server name to use. (str:notset:default:default)
            realtime: Whether to use realtime mode. (bool:set:True:True)
   realtime-priority: Scheduler priority when running in realtime mode. (sint:notset:10:10)
           temporary: Exit once all clients have closed their connections. (bool:notset:False:False)
             verbose: Verbose mode. (bool:set:False:False)
      client-timeout: Client timeout limit in milliseconds. (sint:notset:0:0)
        clock-source: Clocksource type : c(ycle) | h(pet) | s(ystem). (uint:notset:0:0)
            port-max: Maximum number of ports. (uint:notset:2048:2048)
    replace-registry: Replace shared memory registry. (bool:notset:False:False)
                sync: Use server synchronous mode. (bool:set:False:False)
   self-connect-mode: Self connect mode. (char:notset: : )
       slave-drivers: Slave drivers to use (str:notset::)



Internal clients:

jack_control il



Code:
--- internals list
profiler
netmanager
netadapter
audioadapter



OSS / system audio configuration​

/etc/sysctl.conf

Code:
hw.snd.default_unit=4

dev.pcm.4.bitperfect=1
dev.pcm.4.play.vchanrate=48000
dev.pcm.4.play.vchanformat=s16le:2.0
dev.pcm.4.play.vchanmode=fixed

hw.usb.uaudio.default_channels=24
hw.usb.uaudio.default_rate=48000
hw.usb.uaudio.default_bits=24

Question​

How do FreeBSD users typically solve this situation when:

  • the audio interface exposes 24 capture channels
  • audio routing is handled by JACK
  • an application such as Telegram expects a mono microphone device
Possible approaches I am considering:

  • creating a virtual mono microphone device
  • JACK channel mixing
  • OSS virtual devices
  • PulseAudio remapped sources
What is the typical approach used in FreeBSD JACK setups for applications that expect a simple mono microphone device?
 

~ cat /dev/sndstat



Code:
Installed devices:
pcm0: <NVIDIA (0x0084) (HDMI/DP 8ch)> (play)
pcm1: <NVIDIA (0x0084) (HDMI/DP 8ch)> (play)
pcm2: <NVIDIA (0x0084) (HDMI/DP 8ch)> (play)
pcm3: <NVIDIA (0x0084) (HDMI/DP 8ch)> (play)
pcm4: <Soundcraft Soundcraft Signature 22 MTK> (play/rec) default
pcm5: <vendor 0x046d HD Pro Webcam C920> (rec)
No devices installed from userspace.
 
pcm4: <Soundcraft Soundcraft Signature 22 MTK> (play/rec) default
Looks like the problem that I have with a Motu M4, the driver software that came with it is for Windows only.... It has 4 physical inputs and 4 physical outputs, but only one device node /dev/dspN will be created at plugin. BTW, setting hw.snd.verbose=2 in /etc/sysctl.conf gives more detailed info with cat /dev/sndstat.

To get access to all channels you can use virtual_oss. As of FreeBSD version 15.0, virtual_oss is in base. In earlier versions it is in ports, so depending on your FBSD version you may have to install it first. Virtual_oss can create device nodes (/dev/dspN) for the missing channels. In application software you can refer to a device node to access a channel. I don't know your particular use case, but I suppose most channels will be accessed either individually or as a stereo pair. As an example, here is a stripped down version of the script I use to create device nodes /dev/dsp10 to /dev/dsp13:

Code:
#!/bin/sh

sysctl dev.pcm.4.play.vchans=0
sysctl dev.pcm.4.rec.vchans=0
mixer -f /dev/mixer4 vol.volume=1
mixer -f /dev/mixer4 pcm.volume=1

/usr/sbin/virtual_oss \
 -B -S -Q 0 -i 8 -C 24 -c 24 -b 32 -r 48000 -s 4ms -f /dev/dsp4 \
 -m 0,0,1,1 -d dsp10 \
 -m 2,2 -d dsp11 \
 -m 3,3 -a i,4.2 -a o,3.5 -d dsp12 \
 -t vdsp.ctl

First, it sets virtual channels (vchans) to zero and mixer volumes to max. Then it starts virtual_oss, see the man page for decriptions of these and other options. Option -f sets the playback and recording device node. This must be the device node automatically created at plugin. It's /dev/dsp4 in my case, should be /dev/dsp4 (pcm4) too in your case. This device will be in use as long as virtual_oss is running.

The three lines starting with -m create three new device nodes, dsp10, dsp11 and dsp12. Channels are numbered starting with zero, the first line uses channels 0 and 1 (both in and out) as a stereo pair. This will be device node /dev/dsp10. The second line creates /dev/dsp11 for mono channel 2. The third line creates /dev/dsp12 from channel 3 with some added amplification for input and output.

This is a simple script that could work for you as well. It may get you going to access the channels provided by your Soundcraft the way you want. You can find a few more elaborate examples in the man page.

Last but not least, virtual_oss requires cuse.ko. Hence use kldload cuse.ko or put cuse_load="YES" in /boot/loader.conf.
 
Afaik that Soundcraft is a USB audio interface, just like that Motu. You can use these in combination with an audio card, but you don't have to.
The problem with USB audio devices is that they usually come with Windows drivers only. Virtual_oss can solve that problem in most cases.
 
There is a difference between Windows drivers, which will never work under FreeBSD and Windows userlevel applications that talk to the device directly over USB. They usually do configuration tasks such as routing. Such userlevel applications can very well work under Wine. USB access is pretty supported in Wine.
 
Back
Top