125 lines
4.1 KiB
Rust
125 lines
4.1 KiB
Rust
use anyhow::Result;
|
|
use std::fs;
|
|
use std::path::Path;
|
|
use std::sync::Arc;
|
|
use tokio::io::AsyncReadExt;
|
|
use tokio::net::{UnixListener, UnixStream};
|
|
use tokio::sync::Mutex;
|
|
|
|
pub const MIXER_SOCKET_PATH: &str = "/tmp/audio_mixer.sock";
|
|
|
|
/// 混音器服务,负责接收来自多个 Injector 的音频流并进行混音
|
|
pub struct Mixer {
|
|
clients: Arc<Mutex<Vec<UnixStream>>>,
|
|
}
|
|
|
|
impl Mixer {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
clients: Arc::new(Mutex::new(Vec::new())),
|
|
}
|
|
}
|
|
|
|
/// 启动 Unix Socket 监听服务
|
|
pub async fn start(&self) -> Result<()> {
|
|
if Path::new(MIXER_SOCKET_PATH).exists() {
|
|
let _ = fs::remove_file(MIXER_SOCKET_PATH);
|
|
}
|
|
|
|
let listener = UnixListener::bind(MIXER_SOCKET_PATH)?;
|
|
// 允许任何用户写入,确保 ALSA 进程有权连接
|
|
let _ = std::process::Command::new("chmod")
|
|
.arg("666")
|
|
.arg(MIXER_SOCKET_PATH)
|
|
.status();
|
|
|
|
let clients = self.clients.clone();
|
|
|
|
tokio::spawn(async move {
|
|
loop {
|
|
match listener.accept().await {
|
|
Ok((stream, _)) => {
|
|
let mut c = clients.lock().await;
|
|
c.push(stream);
|
|
}
|
|
Err(e) => {
|
|
eprintln!("❌ Mixer accept error: {:?}", e);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// 获取当前连接的客户端数量
|
|
pub async fn client_count(&self) -> usize {
|
|
self.clients.lock().await.len()
|
|
}
|
|
|
|
/// 读取并混合一帧音频数据
|
|
/// frame_size: 每声道的采样点数
|
|
/// channels: 声道数
|
|
pub async fn read_mixed_frame(&self, frame_size: usize, channels: usize) -> Vec<i16> {
|
|
let total_samples = frame_size * channels;
|
|
let bytes_per_frame = total_samples * 2;
|
|
let mut raw_buf = vec![0u8; bytes_per_frame];
|
|
|
|
loop {
|
|
let mut clients = self.clients.lock().await;
|
|
if clients.is_empty() {
|
|
drop(clients);
|
|
// 没有客户端时,稍微等待,避免空转
|
|
tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
|
|
continue;
|
|
}
|
|
|
|
let mut mixed_frame = vec![0i32; total_samples];
|
|
let mut active_clients = Vec::new();
|
|
let mut read_any = false;
|
|
|
|
// 遍历所有客户端,各读取一帧
|
|
for mut client in clients.drain(..) {
|
|
// 注意:这里 read_exact 是异步的,会按顺序等待每个客户端的数据
|
|
// 在主从同步场景下,所有客户端通常来自本地 ALSA,速率是同步的
|
|
match client.read_exact(&mut raw_buf).await {
|
|
Ok(_) => {
|
|
for i in 0..total_samples {
|
|
let sample =
|
|
i16::from_le_bytes([raw_buf[i * 2], raw_buf[i * 2 + 1]]) as i32;
|
|
mixed_frame[i] += sample;
|
|
}
|
|
active_clients.push(client);
|
|
read_any = true;
|
|
}
|
|
Err(_) => {
|
|
// 客户端断开连接
|
|
}
|
|
}
|
|
}
|
|
|
|
*clients = active_clients;
|
|
|
|
if read_any {
|
|
// 饱和截断 (Clipping/Saturating) 将 i32 转回 i16
|
|
return mixed_frame
|
|
.into_iter()
|
|
.map(|x| {
|
|
if x > 32767 {
|
|
32767
|
|
} else if x < -32768 {
|
|
-32768
|
|
} else {
|
|
x as i16
|
|
}
|
|
})
|
|
.collect();
|
|
} else {
|
|
// 如果所有客户端都断开了,继续等待新连接
|
|
drop(clients);
|
|
tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
|
|
}
|
|
}
|
|
}
|
|
}
|