External USB Speakerphone on OpenBSD
2021-12-15This article describes some tests I did with a speakerphone under OpenBSD. I got it working with some restrictions.
Introduction
Within the winter term of 2021/22 I had the chance to get my hands on a portable, wireless speakerphone (EPOS 30T BT). A great opportunity to tinker with such kind of device under OpenBSD. Similar to jcs experience with bt audio[1], I used the include USB Bluetooth sound card to connect the speakerphone with my PC.
In case of success, I would use the device to replace my current USB audio interface/headset combination (yamaha AG 03 and Superlux HDC 660X). In general, I'm using this setup for video conferencing with my students. But first things first, I need to allow to record audio on my machine.
doas echo sysctl.kern.audio.record=1 >> /etc/sysctl.conf
Basic Setup
I don't want to reproduce the information from sndiod[2] and aucat[3]. What follows, therefore, is a summary of commands I used often during my tests.
Basically, all physical (the real hardware) audio devices get numbered like audio0, audio1 and corresponding (raw) sound devices rsnd0, rsnd1. On my system, rsnd0 refers to my internal audio device (e.g. speaker). To test an external audio device I first tried to play and record audio directly on the raw device rsnd/1 using aucat.
doas aucat -f rsnd/1 -o test.wav
doas aucat -f rsnd/1 -i test.wav
What I don't like about this approach is to execute these commands with higher privileges (i.e. root). For that reason, I configured sndiod in a way that it uses the internal audio device rsnd/0 but switches to rsnd/1 when available.
sndiod -dd -f rsnd/0 -F rsnd/1
Again, recording and playing of a .wav file but this time by using snd devices as regular user. But the speakerphone produces no sound (i.e. empty .wav file).
aucat -f snd/1 -o test.wav
aucat -f snd/1 -i test.wav
Side Node: On my system, I always suffered from "shuttering" audio on my speakers, my current workaround is to disable the rec mode on default:
sndiod -f rsnd/0 -m play -s default -F rsnd/1 -m play,rec
Anyhow, the speakerphone keeps silent. So let's tinker with this thing a little bit more.
Testing
For testing, I attached my working yamaha AG 03 interface and start to invest. Here rsnd/1 is the EPOS and rsnd/2 the AG03. As you can see I disabled my internal sound system (damn is sndiod flexible!).
sndiod -dd -f rsnd/1 -f rsnd/2
At that point I came up with a test table as follows:
Device Mode Command
EPOS Record aucat -f snd/1 -o test.wav
EPOS Play aucat -f snd/1 -i test.wav
AG03 Play aucat -f snd/2 -i test.wav
AG03 Record aucat -f snd/2 -o test.wav
Resulting in the following combinations. (BTW. sndiod is really nice in doing such things in the mix)
Record Play Result
EPOS EPOS no audio
EPOS AG03 no audio
AG03 EPOS yes
AG03 AG03 yes
So it seems, that the speakerphone is not recording. As a next step I compared the monitor and control information of both devices to get a hint. After shutting down of sndiod to get access to the raw devices.
Control Information for the AG03 (rsnd/2):
In both cases, I've checked the following points:
- all levels muted off
- all level are high at 1.
Audio
audioctl -f /dev/audio2
name=uaudio1
mode=play
pause=0
active=0
nblks=16
blksz=480
rate=48000
encoding=s24le4msb
play.channels=2
play.bytes=0
play.errors=0
record.channels=2
record.bytes=0
record.errors=0
Mixer
mixerctl -f /dev/audio2
record.enable=sysctl
Control information for the EPOS
Audio
audioctl -f /dev/audio1
name=uaudio0
mode=play
pause=0
active=0
nblks=16
blksz=480
rate=48000
encoding=s16le
play.channels=2
play.bytes=0
play.errors=0
record.channels=1
record.bytes=0
record.errors=0
Mixer settings
mixerctl -f /dev/audioctl1
outputs.dac=128
inputs.record=255
inputs.record_mute=off
record.enable=sysctl
Sessions with sndiod
Maybe session output of sndiod brings more light into that. A regular session with sndiod looks like:
snd0.default: rec=0:1 play=0:1 vol=32768 dup
snd0.0: rec=0:1 play=0:1 vol=32768 dup
snd1.1: rec=0:1 play=0:1 vol=32768 dup
default/server.device=0:1 at 1 -> opt_dev:default/0: added
default/server.device=1:0 at 2 -> opt_dev:default/1: added
app/firefox0.level=127 at 3 -> slot_level:firefox0: added
warning, device opened in play-only mode
0/output0.level=128 at 4 -> hw:0/0: added
0/input0.level=255 at 5 -> hw:0/64: added
0/input0.mute=0 at 6 -> hw:0/96: added
snd0: 48000Hz, s16le, play 0:1, 16 blocks of 480 frames
firefox0: 48000Hz, s16le, play 0:1, 4 blocks of 480 frames
snd0: device started
firefox0: attached at -7680 + 0/480
Screening the logs, warning, device opened in play-only mode is interesting because even if I configured this device in play,rec mode it merely gets opened in play-only mode.
I'm not certain about the reason but had some ideas in mind:
- the device cannot support play,rec (full-duplex)
- the app (i.e. Firefox) just requested a play mode; which is unlikely at least in case for aucat where it was explicitly states as record operation
The last mile with aucat
So back to start, I start recording with aucat like
aucat -f snd/0 -o test.wav
Corresponding sndiod configuration that excludes all other audio devices and just uses the speakerphone.
sndiod -dd -f rsnd/1
And the corresponding log:
snd0.default: rec=0:1 play=0:1 vol=32768 dup
snd0.0: rec=0:1 play=0:1 vol=32768 dup
warning, device opened in play-only mode
0/output0.level=128 at 1 -> hw:0/0: added
0/input0.level=255 at 2 -> hw:0/64: added
0/input0.mute=0 at 3 -> hw:0/96: added
snd0: 48000Hz, s16le, play 0:1, 16 blocks of 480 frames
default/server.device=0:1 at 4 -> opt_dev:default/0: added
app/firefox0.level=127 at 5 -> slot_level:firefox0: added
firefox0: 48000Hz, s16le, play 0:1, 4 blocks of 480 frames
snd0: device started
firefox0: attached at -7680 + 0/480
app/aucat0.level=127 at 6 -> slot_level:aucat0: added
aucat0: attached at -7680 + 0/480
aucat0: 48000Hz, s16le, rec 0:1, 20 blocks of 480 frames
aucat0: detached at 0 + 0/480
aucat0: 48000Hz, s16le, play 0:1, 20 blocks of 480 frames
aucat0: attached at -7680 + 0/480
app/firefox1.level=127 at 7 -> slot_level:firefox1: added
firefox1: 44100Hz, s16le, play 0:1, 10 blocks of 441 frames
firefox1: attached at -7056 + 0/441
aucat0: attached at -7680 + 0/480
aucat0: 48000Hz, s16le, rec 0:1, 20 blocks of 480 frames
Recording works if the mode of the default sub-device is set to rec rather then play,rec
sndiod -dd -f rsnd/1 -m rec -s default
Bringing the speakerphone in rec-only mode. But, you guess it, no audio playing (kind of walky talky)
The warning about play-only mode is also gone (also kind of expected).
snd0.default: rec=0:1 dup
snd0.0: rec=0:1 dup
default/server.device=0:1 at 1 -> opt_dev:default/0: added
app/firefox0.level=127 at 2 -> slot_level:firefox0: added
0/output0.level=128 at 3 -> hw:0/0: added
0/input0.level=255 at 4 -> hw:0/64: added
0/input0.mute=0 at 5 -> hw:0/96: added
snd0: 16000Hz, s16le, rec 0:0, 16 blocks of 160 frames
firefox0: 48000Hz, s16le, play 0:1, 4 blocks of 480 frames
firefox0 at default: mode not allowed on this sub-device
snd0: device started
firefox0: attached at -7680 + 0/480
app/aucat0.level=127 at 6 -> slot_level:aucat0: added
aucat0: 48000Hz, s16le, play 0:1, 20 blocks of 480 frames
aucat0 at default: mode not allowed on this sub-device
aucat0: attached at -7680 + 0/480
^Cfirefox0: detached at 0 + 0/160
default/server.device=0:1 at 1 -> opt_dev:default/0: removed
snd0: device stopped
snd0: software master level control enabled
0/output.level=127 at 7 -> dev_master:0: added
0/output.level=127 at 7 -> dev_master:0: removed
Changing back to play-only mode, the warning is also gone. Seems that it can be used either in play mode or rec mode but not in play,rec mode (full-duplex).
sndiod -dd -f rsnd/1 -m play -s default
The last test I made was to mix the rec-only mode of the speakerphone and play-only mode with the internal speaker or the AG03 (which kind of worked).
sndiod -dd -f rsnd/1 -m rec -s default -f rsnd/0 -m play -s out
aucat -f snd/0 -o file.wav
aucat -f snd/1.out -i file.wav
Lessons Learned
Shortly after my tests I need to bring the speakerphone back. This closes that case to some degree. Nevertheless I would take the following three key elements from that journey.
- sndiod is flexible like hell despite its simplicity
- no need for external tools to start with audio processing (everything you need is in the base system)
- it highly depends on the device what is working (e.g. a Yamaha AG03 works like a charm on my system)
Links
➥ [1]: jcs.org openbsd bt audio➥ [2]: sndiod(8)
➥ [3]: aucat(1)