feat: UAC USB microphone passthrough

- Add UAC1 gadget function (ConfigFS) with optimized endpoint config
- Browser mic capture with Opus encoding via WebCodecs AudioEncoder
- WebSocket audio transport (Opus 64kbps, 100x bandwidth reduction vs raw PCM)
- aplay subprocess for reliable PCM playback to USB gadget
- Settings toggle + ActionBar mic button (hidden when UAC disabled)
- Dynamic PCM device resolution (/proc/asound)
- c_chmask=0 + req_number=4 fixes DWC3 composite isochronous endpoint issue
This commit is contained in:
arounyf
2026-07-23 06:56:53 +00:00
parent 4e32b05124
commit e9bed3688f
27 changed files with 977 additions and 16 deletions

View File

@@ -0,0 +1,36 @@
use std::sync::Arc;
use axum::{extract::State, Json};
use crate::error::Result;
use crate::otg::service::UacConfig;
use crate::state::AppState;
use super::apply::try_apply_lock;
pub async fn get_uac_config(State(state): State<Arc<AppState>>) -> Json<UacConfig> {
Json(state.config.get().uac.clone())
}
pub async fn update_uac_config(
State(state): State<Arc<AppState>>,
Json(req): Json<UacConfig>,
) -> Result<Json<UacConfig>> {
req.validate()?;
let _guard = try_apply_lock(&state.config_apply_locks.otg, "uac")?;
let old_config = (*state.config.get()).clone();
let mut new_config = old_config.clone();
new_config.uac = req;
state
.config
.update(|config| {
config.uac = new_config.uac.clone();
})
.await?;
super::apply::apply_usb_config(&state, &old_config, &new_config).await?;
Ok(Json(state.config.get().uac.clone()))
}