-
Notifications
You must be signed in to change notification settings - Fork 1
H700 port 05 Audio
This chapter records the H700 audio architecture, decisions, and diagnostic evidence. The maintained test report is 08 — Testing status; release criteria and deferred work are in 09 — Roadmap.
SND_init (shared api.c) → SDL audio → ALSA backend → card 0 audiocodec.
SDL_AUDIODRIVER=alsa exported by launch.sh. GBA/SNES/PS1 run full speed with clean
audio and no underruns in H700 bring-up observations; see the testing report for the
current device coverage.
card 0: audiocodec ← speaker / lineout / headphone (the one we use)
card 1: ahubdam
card 2: ahubhdmi ← HDMI audio
Symptom: audio played but was glitchy/"sliced". Cause chain:
- SDL2 was direct-linked against the tg5040 SDK's
libasound, which has no symbol versioning; the SDK's copy of libasound was also bundled at first. - At runtime against the device's (versioned) libasound, the dynamic linker resolved
SDL's unversioned references to the legacy
ALSA_0.9compatibility symbols — which have value semantics, not the modern pointer-in/out semantics — for the wholesnd_pcm_hw_params_set_*family. -
snd_pcm_hw_params_set_rate_neartherefore misbehaved: the codec was clamped to 192 kHz while SDL believed it got 32.768 kHz → resample math produced sliced audio.
Fix (commit 752cefe8): build SDL2 with --enable-alsa-shared so it dlopens the
device's own libasound — dlsym always resolves the default (current) symbol
version. The bundled libasound copy was removed. Only libtinyalsa (used directly by
libmsettings) is bundled. Lesson generalized in 01: prefer dlopen over cross-linking
for libraries that exist on the target.
- Master:
digital volume, a 0–63 attenuator with a reversed scale — code writes100 - valpercent. Confirmed correct by listening; the control's TLV metadata is garbage, so don't trustamixerranges here.lineout volumesecondary. - Mute:
SPKswitch off + saved/restored volume (H700 has no/sys/class/speaker/mute). The settings application enables the mute toggle. - Bring-up evidence: on RG40XXV and RG34XXSP, the volume UI and audible level range behaved as intended from mute through 100%.
- Suspend: the
suspendscript saves the full mixer state (alsactl store) inbefore()and restores it inafter_sync()on resume (an early version had the restore commented out; it's live now).
Hardware behavior: plugging headphones auto-mutes the speaker and routes audio to the jack with no NextUI involvement.
Software: Brick-style SetJack / separate speaker vs headphones volume / HP
volume icon are not wired on h700 — keymon never monitors a jack event, so
GetJack() stays 0 and the volume UI keeps the speaker path. That is cosmetic /
dual-volume behavior only; routing does not depend on it. The HP-icon/independent-
volume enhancement is classified in 09. (jack_state sysfs was
historically always 0 and was never needed once hardware auto-mute was confirmed.)
The clean 2026 RG40XXV stock image contains BlueALSA 4.2.0, its ALSA PCM/control
plugins, /etc/alsa/conf.d/20-bluealsa.conf, and the system D-Bus policy. A live
startup probe confirmed that bluealsa -p a2dp-source acquires org.bluealsa and
registers two SBC source endpoints below /org/bluez/hci0.
H700 now follows the tg5040 lifecycle with a stock-only implementation:
-
bt_init.shstarts BlueALSA only afterhci0and stock BlueZ are ready, waits for its D-Bus name, uses BlueALSA's default volume mode with--initial-volume=100, and logs useful startup failures. It deliberately omits--a2dp-volume, which can leave AirPods at absolute volume zero on this firmware -
/usr/bin/bluealsa, the ALSA PCM/control plugins, SBC runtime, ALSA configuration, and BlueZ all come from the stock firmware; none are bundled or replaced - settings exposes the existing maximum-sampling-rate control (the H700-specific
NO_BT_AUDIObuild gate is removed) - shared
audiomonselects the connected device and writes$HOME/.asoundrc; MinArch's existing device watcher then reopens SDL audio against the stockbluealsaPCM. The H700 build alone omits thedelay 0option rejected by its stock BlueALSA plugin; tg5040 and tg5050 output is unchanged - disabling Bluetooth stops BlueALSA before BlueZ; no
bluetoothd,bluetoothctl, or other BlueZ component is bundled or replaced
RG40XXV / AirPods 4 ANC: pairing, remembered pairing,
manual connection, A2DP routing, SBC transport, and user-audible game audio pass.
Putting the AirPods back in their case disconnects them and restores audio to the
internal speaker as expected.
The original silent stream was not a 44.1/48 kHz problem: BlueZ created the AirPods
transport with absolute volume zero. Setting its MediaTransport1.Volume to 127 made
the already-running stream audible. The shipped path instead starts BlueALSA with an
initial mixer volume and has libmsettings update the connected A2DP mixer control.
An A2DP PCM publication race was also observed during an artificial daemon
restart while the device remained logically connected; it is not handled by an
H700-only shared-code workaround unless a normal connection flow reproduces it.
AirPods auto-connect is intentionally outside NextUI's control: they commonly connect to a nearby paired iPhone or Mac instead. NextUI retains the pairing and provides the reliable manual Connect path. If the stock firmware lacks runnable BlueALSA, the device remains controller-only rather than receiving a replacement Bluetooth audio stack from NextUI.
PLAT_pickSampleRate: tg5040 logic. One H700-specific fix: it must not call
GetAudioSink() (shared-memory settings) — minarch calls it before InitSettings()
maps the shm, which segfaulted; it now consults only PLAT_bluetoothConnected().
Codec natively supports 48000/44100.