more improvements, still choppy tbh
This commit is contained in:
8 files changed
+261
-30
No files matched your search
+27
-15
@@ -1,11 +1,14 @@
|
||||
use crate::input_buffer::InputBuffer;
|
||||
use crate::udp_connection::OpusUdpConnection;
|
||||
use crate::{CIRC_BUFFER_SIZE, SAMPLE_RATE, STEREO_FRAME_SIZE};
|
||||
use crate::{CIRC_BUFFER_SIZE, SAMPLE_RATE, SAMPLE_RATE_RAW, STEREO_FRAME_SIZE};
|
||||
use anyhow::Error;
|
||||
use audiopus::Channels;
|
||||
use audiopus::coder::Decoder;
|
||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
use cpal::{Device, FromSample, Host, I24, Sample, SizedSample, SupportedStreamConfig};
|
||||
use cpal::{
|
||||
BufferSize, Device, FromSample, Host, I24, Sample, SampleRate, SizedSample, StreamConfig,
|
||||
SupportedStreamConfig,
|
||||
};
|
||||
use log::{error, info};
|
||||
use std::io::{ErrorKind, Read};
|
||||
use std::net::SocketAddr;
|
||||
@@ -36,6 +39,10 @@ pub fn listen(device: &str, addr: SocketAddr, host: Host) -> Result<(), Error> {
|
||||
let config = device.default_output_config()?;
|
||||
println!("Default output config : {config:?}");
|
||||
|
||||
for cfg in device.supported_output_configs()? {
|
||||
info!("{:?}", cfg);
|
||||
}
|
||||
|
||||
let decoder = Decoder::new(SAMPLE_RATE, Channels::Stereo)?;
|
||||
|
||||
let input_buffer = InputBuffer::new(STEREO_FRAME_SIZE, decoder);
|
||||
@@ -57,23 +64,28 @@ pub fn listen(device: &str, addr: SocketAddr, host: Host) -> Result<(), Error> {
|
||||
|
||||
fn stream_setup_for(
|
||||
device: Device,
|
||||
config: SupportedStreamConfig,
|
||||
default_config: SupportedStreamConfig,
|
||||
ctx: Context,
|
||||
) -> Result<cpal::Stream, anyhow::Error>
|
||||
where
|
||||
{
|
||||
match config.sample_format() {
|
||||
cpal::SampleFormat::I8 => make_stream::<i8>(&device, &config.into(), ctx),
|
||||
cpal::SampleFormat::I16 => make_stream::<i16>(&device, &config.into(), ctx),
|
||||
cpal::SampleFormat::I24 => make_stream::<I24>(&device, &config.into(), ctx),
|
||||
cpal::SampleFormat::I32 => make_stream::<i32>(&device, &config.into(), ctx),
|
||||
cpal::SampleFormat::I64 => make_stream::<i64>(&device, &config.into(), ctx),
|
||||
cpal::SampleFormat::U8 => make_stream::<u8>(&device, &config.into(), ctx),
|
||||
cpal::SampleFormat::U16 => make_stream::<u16>(&device, &config.into(), ctx),
|
||||
cpal::SampleFormat::U32 => make_stream::<u32>(&device, &config.into(), ctx),
|
||||
cpal::SampleFormat::U64 => make_stream::<u64>(&device, &config.into(), ctx),
|
||||
cpal::SampleFormat::F32 => make_stream::<f32>(&device, &config.into(), ctx),
|
||||
cpal::SampleFormat::F64 => make_stream::<f64>(&device, &config.into(), ctx),
|
||||
let mut config: StreamConfig = default_config.clone().into();
|
||||
|
||||
config.sample_rate = SampleRate(SAMPLE_RATE_RAW as u32);
|
||||
config.buffer_size = BufferSize::Fixed(STEREO_FRAME_SIZE as u32);
|
||||
|
||||
match default_config.sample_format() {
|
||||
cpal::SampleFormat::I8 => make_stream::<i8>(&device, &config, ctx),
|
||||
cpal::SampleFormat::I16 => make_stream::<i16>(&device, &config, ctx),
|
||||
cpal::SampleFormat::I24 => make_stream::<I24>(&device, &config, ctx),
|
||||
cpal::SampleFormat::I32 => make_stream::<i32>(&device, &config, ctx),
|
||||
cpal::SampleFormat::I64 => make_stream::<i64>(&device, &config, ctx),
|
||||
cpal::SampleFormat::U8 => make_stream::<u8>(&device, &config, ctx),
|
||||
cpal::SampleFormat::U16 => make_stream::<u16>(&device, &config, ctx),
|
||||
cpal::SampleFormat::U32 => make_stream::<u32>(&device, &config, ctx),
|
||||
cpal::SampleFormat::U64 => make_stream::<u64>(&device, &config, ctx),
|
||||
cpal::SampleFormat::F32 => make_stream::<f32>(&device, &config, ctx),
|
||||
cpal::SampleFormat::F64 => make_stream::<f64>(&device, &config, ctx),
|
||||
sample_format => Err(anyhow::Error::msg(format!(
|
||||
"Unsupported sample format '{sample_format}'"
|
||||
))),
|
||||
|
||||
+19
-9
@@ -1,11 +1,14 @@
|
||||
use crate::output_buffer::OutputBuffer;
|
||||
use crate::udp_connection::OpusUdpConnection;
|
||||
use crate::{CIRC_BUFFER_SIZE, SAMPLE_RATE, SAMPLE_RATE_RAW, STEREO_FRAME_SIZE};
|
||||
use crate::{
|
||||
CIRC_BUFFER_SIZE, SAMPLE_RATE, SAMPLE_RATE_RAW,
|
||||
STEREO_FRAME_SIZE,
|
||||
};
|
||||
use anyhow::Error;
|
||||
use audiopus::coder::Encoder;
|
||||
use audiopus::{Application, Channels};
|
||||
use audiopus::{Application, Bitrate, Channels, Signal};
|
||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
use cpal::{FromSample, Host, Sample, StreamConfig};
|
||||
use cpal::{BufferSize, FromSample, Host, Sample, StreamConfig};
|
||||
use log::{error, info};
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -33,11 +36,19 @@ pub fn tap(
|
||||
|
||||
let mut stream_config = StreamConfig::from(config.clone());
|
||||
stream_config.sample_rate = cpal::SampleRate(SAMPLE_RATE_RAW as u32);
|
||||
stream_config.buffer_size = BufferSize::Fixed(STEREO_FRAME_SIZE as u32);
|
||||
|
||||
for cfg in device.supported_input_configs()? {
|
||||
info!("{:?}", cfg)
|
||||
}
|
||||
|
||||
info!("Stream config: {stream_config:?}");
|
||||
|
||||
let udp_connection = OpusUdpConnection::new(src_addr, dest_addr)?;
|
||||
let encoder = Encoder::new(SAMPLE_RATE, Channels::Stereo, Application::Audio)?;
|
||||
let mut encoder = Encoder::new(SAMPLE_RATE, Channels::Stereo, Application::Audio)?;
|
||||
encoder.set_bitrate(Bitrate::Max)?;
|
||||
encoder.set_complexity(10)?;
|
||||
encoder.set_signal(Signal::Music)?;
|
||||
|
||||
let sound_buffer = Arc::new(Mutex::new(OutputBuffer::new(
|
||||
STEREO_FRAME_SIZE,
|
||||
@@ -98,11 +109,10 @@ fn write_input_data<const N: usize, T>(
|
||||
T: Sample,
|
||||
f32: FromSample<T>,
|
||||
{
|
||||
if let Ok(mut ctx) = ctx.try_lock() {
|
||||
let sample: Vec<f32> = input.iter().map(|s| f32::from_sample(*s)).collect();
|
||||
let mut ctx = ctx.lock().expect("Mutex poisoned");
|
||||
let sample: Vec<f32> = input.iter().map(|s| f32::from_sample(*s)).collect();
|
||||
|
||||
if let Err(err) = ctx.write_data(&sample) {
|
||||
error!("Got error when writing data to sound buffer: {err}")
|
||||
}
|
||||
if let Err(err) = ctx.write_data(&sample) {
|
||||
error!("Got error when writing data to sound buffer: {err}")
|
||||
}
|
||||
}
|
||||
+27
-1
@@ -1,8 +1,11 @@
|
||||
use crate::tap_packet::{TapPacketError, TapPacketHeader};
|
||||
use audiopus::MutSignals;
|
||||
use audiopus::coder::Decoder;
|
||||
use audiopus::packet::Packet;
|
||||
use chrono::Utc;
|
||||
use circular_buffer::CircularBuffer;
|
||||
use cpal::{FromSample, Sample};
|
||||
use log::warn;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
@@ -12,6 +15,8 @@ pub enum Error {
|
||||
IoError(#[from] std::io::Error),
|
||||
#[error("Decode error: {0}")]
|
||||
DecodeError(#[from] audiopus::Error),
|
||||
#[error("Tap packet error: {0}")]
|
||||
TapPacketError(#[from] TapPacketError),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -19,6 +24,7 @@ pub struct InputBuffer<const N: usize> {
|
||||
circular_buffer: CircularBuffer<N, f32>,
|
||||
frame_size: usize,
|
||||
decoder: Decoder,
|
||||
expected_seq: u32,
|
||||
}
|
||||
|
||||
impl<const N: usize> InputBuffer<N> {
|
||||
@@ -27,11 +33,31 @@ impl<const N: usize> InputBuffer<N> {
|
||||
circular_buffer: CircularBuffer::new(),
|
||||
frame_size,
|
||||
decoder,
|
||||
expected_seq: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn decode(&mut self, input_buffer: &[u8], output_buffer: &mut [f32]) -> Result<usize, Error> {
|
||||
let packet = Packet::try_from(input_buffer)?;
|
||||
let hdr = TapPacketHeader::from_buffer(input_buffer)?;
|
||||
|
||||
if !hdr.check_acceptable_latency_tolerance() {
|
||||
warn!(
|
||||
"Packet outside of tolerance: packet.timestamp={} time={}",
|
||||
hdr.timestamp,
|
||||
Utc::now().timestamp()
|
||||
);
|
||||
}
|
||||
|
||||
if hdr.seq != self.expected_seq {
|
||||
warn!(
|
||||
"Found sequence mismatch, expected seq={}, {hdr:?}",
|
||||
self.expected_seq
|
||||
);
|
||||
}
|
||||
|
||||
(self.expected_seq, _) = hdr.seq.overflowing_add(1);
|
||||
|
||||
let packet = Packet::try_from(&input_buffer[TapPacketHeader::serialized_size()..])?;
|
||||
|
||||
let signals = MutSignals::try_from(output_buffer)?;
|
||||
Ok(self.decoder.decode_float(Some(packet), signals, false)?)
|
||||
|
||||
@@ -2,6 +2,7 @@ mod audio_sink;
|
||||
mod audio_source;
|
||||
mod input_buffer;
|
||||
mod output_buffer;
|
||||
mod tap_packet;
|
||||
mod udp_connection;
|
||||
mod util;
|
||||
|
||||
|
||||
+19
-3
@@ -1,3 +1,4 @@
|
||||
use crate::tap_packet::{TapPacketError, TapPacketHeader};
|
||||
use audiopus::coder::Encoder;
|
||||
use circular_buffer::CircularBuffer;
|
||||
use std::io::Write;
|
||||
@@ -12,6 +13,8 @@ pub enum Error {
|
||||
IoError(#[from] std::io::Error),
|
||||
#[error("Encode error: {0}")]
|
||||
EncodeError(#[from] audiopus::Error),
|
||||
#[error("Tap packet error: {0}")]
|
||||
TapPacketError(#[from] TapPacketError),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -20,6 +23,7 @@ pub struct OutputBuffer<const N: usize, T: Write> {
|
||||
frame_size: usize,
|
||||
encoder: Encoder,
|
||||
offload: T,
|
||||
seq_count: u32,
|
||||
}
|
||||
|
||||
impl<const N: usize, T: Write> OutputBuffer<N, T> {
|
||||
@@ -29,6 +33,7 @@ impl<const N: usize, T: Write> OutputBuffer<N, T> {
|
||||
frame_size,
|
||||
encoder,
|
||||
offload,
|
||||
seq_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,10 +43,21 @@ impl<const N: usize, T: Write> OutputBuffer<N, T> {
|
||||
}
|
||||
|
||||
fn offload_buffer_data(&mut self) -> Result<(), Error> {
|
||||
let mut output_buffer = vec![0; 32 * 1024];
|
||||
let size = self.encode(&mut output_buffer)?;
|
||||
self.offload.write_all(&output_buffer[0..size])?;
|
||||
let mut output_buffer = vec![0; 32 * 1024 + TapPacketHeader::serialized_size()];
|
||||
let size = self.encode(&mut output_buffer[TapPacketHeader::serialized_size()..])?;
|
||||
|
||||
let hdr = TapPacketHeader {
|
||||
seq: self.seq_count,
|
||||
timestamp: chrono::Utc::now().timestamp(),
|
||||
};
|
||||
|
||||
hdr.write_to_buffer(&mut output_buffer)?;
|
||||
|
||||
let total_size = TapPacketHeader::serialized_size() + size;
|
||||
|
||||
self.offload.write_all(&output_buffer[0..total_size])?;
|
||||
|
||||
(self.seq_count, _) = self.seq_count.overflowing_add(1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
use chrono::Utc;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TapPacketError {
|
||||
#[error("Buffer is too small to serialize data into")]
|
||||
BufTooSmall,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TapPacketHeader {
|
||||
pub seq: u32,
|
||||
pub timestamp: i64,
|
||||
}
|
||||
|
||||
impl TapPacketHeader {
|
||||
pub fn write_to_buffer(&self, buf: &mut [u8]) -> Result<(), TapPacketError> {
|
||||
if buf.len() < Self::serialized_size() {
|
||||
return Err(TapPacketError::BufTooSmall);
|
||||
}
|
||||
|
||||
buf[0..size_of::<u32>()].copy_from_slice(self.seq.to_be_bytes().as_slice());
|
||||
buf[size_of::<u32>()..Self::serialized_size()]
|
||||
.copy_from_slice(self.timestamp.to_be_bytes().as_slice());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check_acceptable_latency_tolerance(&self) -> bool {
|
||||
Utc::now().timestamp().abs_diff(self.timestamp) < 1
|
||||
}
|
||||
|
||||
pub fn from_buffer(buf: &[u8]) -> Result<Self, TapPacketError> {
|
||||
if buf.len() < Self::serialized_size() {
|
||||
return Err(TapPacketError::BufTooSmall);
|
||||
}
|
||||
|
||||
let mut seq_bytes = [0u8; size_of::<u32>()];
|
||||
seq_bytes.copy_from_slice(&buf[0..size_of::<u32>()]);
|
||||
let seq = u32::from_be_bytes(seq_bytes);
|
||||
|
||||
let mut timestamp_bytes = [0u8; size_of::<i64>()];
|
||||
timestamp_bytes.copy_from_slice(&buf[size_of::<u32>()..Self::serialized_size()]);
|
||||
let timestamp = i64::from_be_bytes(timestamp_bytes);
|
||||
|
||||
Ok(Self { seq, timestamp })
|
||||
}
|
||||
|
||||
pub fn serialized_size() -> usize {
|
||||
size_of::<u32>() + size_of::<i64>()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user