attempt desktop audio playback

This commit is contained in:
2024-11-12 20:03:58 -07:00
parent b65ec274d8
commit b2ee911c66
5 changed files with 354 additions and 59 deletions
+80 -8
View File
@@ -1,11 +1,15 @@
use crate::app::Command;
use color_eyre::eyre::Error;
use color_eyre::eyre::{eyre, Error};
use cpal::traits::{DeviceTrait, HostTrait};
use crossbeam_queue::ArrayQueue;
use dioxus::hooks::{UnboundedReceiver, UnboundedSender};
use futures::io::{AsyncRead, AsyncWrite};
use mumble_protocol::control::{ClientControlCodec, ControlPacket};
use mumble_protocol::Serverbound;
use mumble_web2_common::GuiConfig;
use std::collections::VecDeque;
use std::net::ToSocketAddrs;
use std::sync::Mutex;
use std::{fmt, io, sync::Arc};
use tokio::net::TcpStream;
use tokio_rustls::rustls;
@@ -15,6 +19,7 @@ use tokio_rustls::rustls::ClientConfig;
use tokio_rustls::rustls::DigitallySignedStruct;
use tokio_rustls::TlsConnector;
use tokio_util::compat::{TokioAsyncReadCompatExt as _, TokioAsyncWriteCompatExt as _};
use tracing::{error, warn};
pub use tokio::task::spawn;
pub use tokio::time::sleep;
@@ -25,25 +30,92 @@ impl<T: AsyncRead + Unpin + Send + 'static> ImpRead for T {}
pub trait ImpWrite: AsyncWrite + Unpin + Send + 'static {}
impl<T: AsyncWrite + Unpin + Send + 'static> ImpWrite for T {}
pub struct AudioSystem();
pub struct AudioSystem {
output: cpal::Device,
input: cpal::Device,
}
const BUF_LEN: usize = 480; // 20 ms
impl AudioSystem {
pub fn new(sender: UnboundedSender<ControlPacket<Serverbound>>) -> Result<Self, Error> {
pub fn new() -> Result<Self, Error> {
// TODO
Ok(AudioSystem())
let host = cpal::default_host();
let name = host.id();
Ok(AudioSystem {
output: host
.default_output_device()
.ok_or(eyre!("no output devices from {name:?}"))?,
input: host
.default_input_device()
.ok_or(eyre!("no input devices from {name:?}"))?,
})
}
pub fn start_recording(&mut self, each: impl FnMut(Vec<u8>) + 'static) -> Result<(), Error> {
// TODO
Ok(())
}
pub fn create_player(&mut self) -> Result<AudioPlayer, Error> {
// TODO
Ok(AudioPlayer())
let queue = Arc::new(ArrayQueue::<[i16; BUF_LEN]>::new(10));
let decoder = opus::Decoder::new(48_000, opus::Channels::Mono)?;
let stream = {
let queue = queue.clone();
self.output.build_output_stream(
&cpal::StreamConfig {
channels: 1,
sample_rate: cpal::SampleRate(48_000),
buffer_size: cpal::BufferSize::Fixed(BUF_LEN as u32), // 20ms playback delay
},
move |out, info| match queue.pop() {
Some(buf) => out.copy_from_slice(&buf[..]),
None => out.fill(0),
},
move |err| error!("could not create output stream {err:?}"),
None,
)?
};
Ok(AudioPlayer {
decoder,
stream,
queue,
tmp: vec![0; 2400],
pos: 0,
})
}
}
pub struct AudioPlayer();
pub struct AudioPlayer {
decoder: opus::Decoder,
stream: cpal::Stream,
queue: Arc<ArrayQueue<[i16; BUF_LEN]>>,
tmp: Vec<i16>,
pos: usize,
}
impl AudioPlayer {
pub fn play_opus(&mut self, payload: &[u8]) {
// TODO
match self
.decoder
.decode(payload, &mut self.tmp[self.pos..], false)
{
Ok(l) => {
self.pos += l;
}
Err(e) => {
error!("opus decode error {e:?}");
}
};
while self.pos >= BUF_LEN {
let mut chunk = [0; BUF_LEN];
chunk.copy_from_slice(&self.tmp[..BUF_LEN]);
dbg!(&chunk);
let _ = self.queue.push(chunk);
let i = std::cell::Cell::new(0usize);
self.tmp.retain(|_| i.replace(i.get() + 1) >= BUF_LEN);
self.pos -= BUF_LEN;
}
}
}