use std::collections::VecDeque;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{self, Receiver, RecvTimeoutError};
use std::sync::Arc;
use std::thread::JoinHandle;
use std::time::Duration;
use wasapi::{
initialize_mta, AudioClient, DeviceEnumerator, Direction, Role, SampleType, StreamMode, WaveFormat,
};
use crate::pcm::{self, SAMPLE_RATE};
use crate::source::{Capture, CaptureError};
struct Worker {
rx: Receiver<Result<Vec<i16>, String>>,
stop: Arc<AtomicBool>,
thread: Option<JoinHandle<()>>,
}
impl Worker {
fn spawn(open: impl FnOnce() -> Result<AudioClient, String> + Send + 'static) -> Result<Self, String> {
let (tx, rx) = mpsc::channel();
let (ready_tx, ready_rx) = mpsc::channel();
let stop = Arc::new(AtomicBool::new(false));
let stop_flag = stop.clone();
let thread = std::thread::Builder::new()
.name("meet-capture".into())
.spawn(move || {
let _ = initialize_mta();
let started = open().and_then(|mut client| start(&mut client).map(|fmt| (client, fmt)));
let (client, format) = match started {
Ok(v) => {
let _ = ready_tx.send(Ok(()));
v
}
Err(e) => {
let _ = ready_tx.send(Err(e));
return;
}
};
if let Err(e) = pump(&client, format, &tx, &stop_flag) {
let _ = tx.send(Err(e));
}
let _ = client.stop_stream();
})
.map_err(|e| e.to_string())?;
ready_rx
.recv()
.map_err(|_| "поток захвата не запустился".to_string())??;
Ok(Self {
rx,
stop,
thread: Some(thread),
})
}
fn read(&mut self) -> Result<Vec<i16>, String> {
let mut out = Vec::new();
match self.rx.recv_timeout(Duration::from_millis(50)) {
Ok(block) => out.extend(block?),
Err(RecvTimeoutError::Timeout) => return Ok(out),
Err(RecvTimeoutError::Disconnected) => return Err("поток захвата остановился".into()),
}
while let Ok(block) = self.rx.try_recv() {
out.extend(block?);
}
Ok(out)
}
}
impl Drop for Worker {
fn drop(&mut self) {
self.stop.store(true, Ordering::Relaxed);
if let Some(t) = self.thread.take() {
let _ = t.join();
}
}
}
#[derive(Clone, Copy)]
enum Format {
Native,
Float { rate: u32, channels: usize },
}
fn start(client: &mut AudioClient) -> Result<Format, String> {
let mode = StreamMode::EventsShared {
autoconvert: true,
buffer_duration_hns: 2_000_000,
};
let native = WaveFormat::new(16, 16, &SampleType::Int, SAMPLE_RATE as usize, 1, None);
let format = match client.initialize_client(&native, &Direction::Capture, &mode) {
Ok(()) => Format::Native,
Err(first) => {
let float = WaveFormat::new(32, 32, &SampleType::Float, 48_000, 2, None);
client
.initialize_client(&float, &Direction::Capture, &mode)
.map_err(|second| format!("{first}; запасной формат: {second}"))?;
Format::Float {
rate: 48_000,
channels: 2,
}
}
};
Ok(format)
}
fn pump(
client: &AudioClient,
format: Format,
tx: &mpsc::Sender<Result<Vec<i16>, String>>,
stop: &AtomicBool,
) -> Result<(), String> {
let event = client.set_get_eventhandle().map_err(|e| e.to_string())?;
let capture = client.get_audiocaptureclient().map_err(|e| e.to_string())?;
client.start_stream().map_err(|e| e.to_string())?;
let mut raw: VecDeque<u8> = VecDeque::new();
while !stop.load(Ordering::Relaxed) {
let _ = event.wait_for_event(100);
loop {
let frames = capture
.get_next_packet_size()
.map_err(|e| e.to_string())?
.unwrap_or(0);
if frames == 0 {
break;
}
let before = raw.len();
let info = capture
.read_from_device_to_deque(&mut raw)
.map_err(|e| e.to_string())?;
if info.flags.silent {
for b in raw.iter_mut().skip(before) {
*b = 0;
}
}
}
if raw.is_empty() {
continue;
}
let bytes: Vec<u8> = raw.drain(..).collect();
let samples = match format {
Format::Native => bytes
.as_chunks::<2>()
.0
.iter()
.map(|c| i16::from_le_bytes(*c))
.collect(),
Format::Float { rate, channels } => {
let floats: Vec<f32> = bytes
.as_chunks::<4>()
.0
.iter()
.map(|c| f32::from_le_bytes(*c))
.collect();
pcm::to_native(&floats, channels, rate)
}
};
if tx.send(Ok(samples)).is_err() {
break;
}
}
Ok(())
}
pub struct SystemAudio(Worker);
impl SystemAudio {
pub fn open() -> Result<Self, CaptureError> {
let pid = std::process::id();
Worker::spawn(move || {
AudioClient::new_application_loopback_client(pid, false).map_err(|e| e.to_string())
})
.map(Self)
.map_err(CaptureError::Loopback)
}
}
impl Capture for SystemAudio {
fn read(&mut self) -> Result<Vec<i16>, CaptureError> {
self.0.read().map_err(CaptureError::Loopback)
}
}
pub struct Microphone(Worker);
impl Microphone {
pub fn open() -> Result<Self, CaptureError> {
Worker::spawn(|| {
let enumerator = DeviceEnumerator::new().map_err(|e| e.to_string())?;
let device = enumerator
.get_default_device_for_role(&Direction::Capture, &Role::Communications)
.or_else(|_| enumerator.get_default_device(&Direction::Capture))
.map_err(|e| e.to_string())?;
device.get_iaudioclient().map_err(|e| e.to_string())
})
.map(Self)
.map_err(CaptureError::Microphone)
}
}
impl Capture for Microphone {
fn read(&mut self) -> Result<Vec<i16>, CaptureError> {
self.0.read().map_err(CaptureError::Microphone)
}
}