feat: audio stack working — pulseaudio + x6200_control + voice_rec

- Add pulseaudio in system mode
- Add alsactl restore with correct mixer state from Aether
- Add x6200-control package (I2C hardware control library)
- Audio chain: pulseaudio -> x6200_control_init -> voice_rec -> speaker
This commit is contained in:
Joakim
2026-05-05 16:03:48 +02:00
parent a5b9efae15
commit c500e75417
12 changed files with 937 additions and 3 deletions

View File

@@ -0,0 +1,88 @@
# direct mix for pcm playback
# aplay -D mixplayback xxx.wav
pcm.mixout {
type dmix
ipc_key 1024
slave {
# slave PCM name
pcm "hw:0,0"
# PCM params
format S16_LE # STR
rate 16000 # INT
#rate 48000 # INT
channels 2 # INT
# stream params,buffer_size=period x period_size
periods 100 # INT,when buffer_size or buffer_time is not specified
period_size 1024 # INT,in frames,3 times than app need
# optional params
# period_time 0 # INT,in usec
# buffer_time 0 # INT,in usec
# buffer_size 65536 #INT,in frames
}
bindings {
0 0 # from 0 -> 0
1 1 # from 1 -> 1
}
# slowptr BOOL # slow but more precise pointer updates
}
# dsnoop for capture
# arecord -D mixcapture test.wav
pcm.mixin {
type dsnoop
ipc_key 1025 # must be unique for all dmix plugins!!!!
# ipc_key_add_uid yes # no need for embedded system
slave {
# slave PCM name
pcm "hw:0,0"
# PCM params
format S16_LE # STR
rate 16000 # INT
#rate 48000 # INT
channels 2 # INT
# stream params,buffer_size=period x period_size
periods 100 # INT,when buffer_size or buffer_time is not specified
period_size 1024 # INT,in frames,3 times than app need
# optional params
# period_time 0 # INT,in usec
# buffer_time 0 # INT,in usec
# buffer_size 65536 #INT,in frames
}
bindings {
0 0 # from 0 -> 0
1 1 # from 1 -> 1
}
}
# pcm plug for playback
pcm.mixplayback {
type plug
slave.pcm "mixout"
# A hint is required for listing the device in some GUIs
hint {
show on
description "X6200 playback mixer"
}
}
# pcm plug for capture
pcm.mixcapture {
type plug
slave.pcm "mixin"
# A hint is required for listing the device in some GUIs
hint {
show on
description "X6200 capture mixer"
}
}
ctl.mixer0 {
type hw
card 0
}

View File

@@ -0,0 +1,40 @@
#!/bin/sh
#
# Alsa config
#
source /etc/profile
start() {
printf "Restore alsa"
alsactl restore
echo "OK"
}
stop() {
printf "Store alsa"
# alsactl store
echo "OK"
}
restart() {
stop
start
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart|reload)
restart
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit $?

View File

@@ -0,0 +1,45 @@
#!/bin/sh
#
# Starts pulseaudio.
#
start() {
printf "Starting pulseaudio: "
umask 007
/usr/bin/pulseaudio \
--system \
--daemonize \
--disallow-exit \
--exit-idle-time=-1 \
--use-pid-file \
--disable-shm
echo "OK"
}
stop() {
printf "Stopping pulseaudio: "
PULSE_RUNTIME_PATH=/var/run/pulse /usr/bin/pulseaudio --kill
echo "OK"
}
restart() {
stop
start
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart|reload)
restart
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit $?

View File

@@ -0,0 +1,59 @@
#!/bin/sh
# S70mestre-audio — X6200 audio hardware init
#
# Initializes the X6200's audio routing via I2C:
# 1. Calls x6200_control_init() to establish MCU state
# 2. Sets speaker volume
# 3. Switches audio routing from RX-radio to SoC DAC (voice_rec)
#
# Must run after S50pulseaudio (pulseaudio must hold PCM open first
# to keep ALSA clocks active).
#
# The voice_rec bit (0x00008 in sple_atue_trx register, cmd 12)
# switches the X6200's internal audio mux from the RF receiver
# to the SoC DAC — allowing piHPSDR/aplay to be heard in the speaker.
VOLUME=50
do_init() {
python3 << EOF
import ctypes
lib = ctypes.CDLL("libaether_x6200_control.so")
lib.x6200_control_init.restype = ctypes.c_bool
lib.x6200_control_rxvol_set.argtypes = [ctypes.c_uint8]
lib.x6200_control_spmode_set.argtypes = [ctypes.c_bool]
if not lib.x6200_control_init():
raise SystemExit("x6200_control_init() failed")
lib.x6200_control_rxvol_set($VOLUME)
lib.x6200_control_spmode_set(False) # Speaker mode (not headphone)
EOF
}
do_voice_rec() {
# Switch audio routing: RX-radio -> SoC DAC (voice_rec bit = 0x00008)
i2ctransfer -y 0 w6@0x72 0x00 0x30 0x08 0x00 0x00 0x00
}
case "$1" in
start)
echo "Starting mestre-audio"
do_init && do_voice_rec && echo "OK" || echo "FAILED"
;;
stop)
echo "Stopping mestre-audio"
# Switch back to RX-radio audio
i2ctransfer -y 0 w6@0x72 0x00 0x30 0x00 0x00 0x00 0x00
echo "OK"
;;
restart)
$0 stop
sleep 1
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart}" >&2
exit 1
;;
esac

View File

@@ -0,0 +1,23 @@
#!/bin/sh
# S96keepalive — X6200 PMU keepalive
#
# The X6200 powers itself off after ~180s unless the AXP PMU receives
# periodic I2C traffic. This script starts the keepalive daemon at boot.
case "$1" in
start)
echo "Starting keepalive"
/usr/sbin/i2c_keepalive.sh >/dev/null 2>&1 &
;;
stop)
echo "Stopping keepalive"
killall i2c_keepalive.sh 2>/dev/null || true
;;
restart)
$0 stop; sleep 1; $0 start
;;
*)
echo "Usage: $0 {start|stop|restart}" >&2
exit 1
;;
esac

View File

@@ -0,0 +1,70 @@
#!/usr/bin/pulseaudio -nF
#
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# PulseAudio is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
# This startup script is used only if PulseAudio is started in system
# mode.
### Automatically restore the volume of streams and devices
load-module module-device-restore
load-module module-stream-restore
load-module module-card-restore
load-module module-switch-on-connect
load-module module-bluez5-device
### Automatically load driver modules depending on the hardware available
.ifexists module-udev-detect.so
load-module module-udev-detect
.else
### Use the static hardware detection module (for systems that lack udev/hal support)
load-module module-detect
.endif
### Load several protocols
.ifexists module-esound-protocol-unix.so
load-module module-esound-protocol-unix
.endif
load-module module-native-protocol-unix auth-anonymous=1
### Automatically restore the default sink/source when changed by the user
### during runtime
### NOTE: This should be loaded as early as possible so that subsequent modules
### that look up the default sink/source get the right value
load-module module-default-device-restore
### Make sure we always have a sink around, even if it is a null sink.
load-module module-always-sink
### Automatically suspend sinks/sources that become idle for too long
load-module module-suspend-on-idle
### Enable positioned event sounds
load-module module-position-event-sounds
### Automatically load driver modules for Bluetooth hardware
.ifexists module-bluetooth-policy.so
load-module module-bluetooth-policy
.endif
.ifexists module-bluetooth-discover.so
load-module module-bluetooth-discover
.endif
### Allow including a system.pa.d directory, which if present, can be used
### for additional configuration snippets.
### Note that those snippet files must have a .pa file extension, not .conf
.nofail
.include /etc/pulse/system.pa.d