use std::slice;
use std::sync::mpsc;
use anyhow::Context;
use log::{debug, error, info};
use crate::common::builder::ChunkBuilder;
use crate::common::callback::AudioCallback;
use crate::common::chunk::Chunk;
use crate::common::result::{Error, Result};
use crate::common::source::AudioSource;
use crate::platform::platform::link::{LinkState, TIMEOUT_MS};
use crate::platform::windows::link::PACKET_FRAMES;
use crate::platform::windows::manager::Command;
#[derive(Debug)]
pub enum Response {
Ready,
Error(Error),
}
pub fn start_stream(
sender: &mpsc::Sender<Response>,
receiver: &mpsc::Receiver<Command>,
sources: &[AudioSource],
callback: &AudioCallback,
) {
let mut links = Vec::with_capacity(sources.len());
let mut mixdown = ChunkBuilder::<f32, PACKET_FRAMES>::new();
let mut builder = ChunkBuilder::new_f32();
for source in sources {
match LinkState::link(source)
.with_context(|| format!("Failed to link to source id={}, name={}", source.id, source.name))
{
Ok(link) => links.push(link),
Err(e) => {
send_logged!(sender, Response::Error(e));
return;
}
}
}
send_logged!(sender, Response::Ready);
debug!("using source id={}, name={} as clock", sources[0].id, sources[0].name);
match || -> Result<()> {
loop {
links[0].event_handle.wait_for_event(TIMEOUT_MS)?;
if matches!(receiver.try_recv(), Ok(Command::Unlink)) {
break Ok(());
}
for link in &mut links {
link.capture_client
.read_from_device(link.buffer.unfilled_mut())
.context("Failed to read audio data")?;
}
stereo_mixdown(&links, &mut mixdown);
if let Some(data) = builder.extend_from_slice(mixdown.data.as_slice()) {
callback(Chunk::from(&builder));
builder.reset_with_overflow(data);
}
}
}() {
Ok(()) => info!("stream stopped"),
Err(e) => error!("stream stopped due to error: {e}"),
}
}
fn stereo_mixdown(chunks: &[LinkState], mixdown: &mut ChunkBuilder<f32, PACKET_FRAMES>) {
for (i, chunk) in chunks.iter().enumerate() {
let samples = unsafe {
slice::from_raw_parts(chunk.buffer.data.as_ptr().cast::<f32>(), PACKET_FRAMES)
};
if i == 0 {
mixdown.data.copy_from_slice(samples);
}
else if i == chunks.len() - 1 {
for (j, sample) in samples.iter().enumerate() {
let sum = mixdown.data[j] + sample;
mixdown.data[j] = sum.tanh();
}
}
else {
for (j, sample) in samples.iter().enumerate() {
mixdown.data[j] += sample;
}
}
}
}