removing device selection filters; possibly converting to 48khz input
Continuous integration / Check (push) Failing after 52s
Continuous integration / Test Suite (push) Failing after 32s
Continuous integration / Rustfmt (push) Successful in 29s
Continuous integration / Clippy (push) Failing after 32s
Continuous integration / build (push) Failing after 33s

This commit is contained in:
2026-10-19 23:29:00 +02:00
parent 0988ada8ac
commit 687ef4ff2c
5 changed files with 228 additions and 130 deletions
+2
View File
@@ -18,3 +18,5 @@ crossterm = "0.29.0"
gag = "1.0.0"
tokio-util = { version = "0.7.19", features = ["codec"]}
futures = "0.3.34"
rubato = "5.0.0"
audioadapter-buffers = "5.2.0"
+54 -30
View File
@@ -1,5 +1,7 @@
use audioadapter_buffers::direct::InterleavedSlice;
use cpal::{
Device, FromSample, Sample, SampleFormat, SizedSample, StreamConfig, SupportedStreamConfig,
Device, FromSample, SAMPLE_RATE_48K, Sample, SampleFormat, SizedSample, StreamConfig,
SupportedStreamConfig,
traits::{DeviceTrait, StreamTrait},
};
use ringbuf::{
@@ -9,6 +11,7 @@ use ringbuf::{
wrap::caching::Caching,
};
use ropus::Encoder;
use rubato::{Async, FixedAsync, PolynomialDegree, Resampler, audioadapter::Adapter};
use shared::AudioStreamInfo;
use std::sync::{Arc, RwLock};
use tokio::{
@@ -318,16 +321,26 @@ pub async fn audio_stream_spawner(
input_stream.play().expect("failed to play input stream");
let samples_needed = ((20.0 / 1000.0)
* input_device_config.sample_rate() as f32
* input_device_config.channels() as f32) as usize;
let frames_needed = ((20.0 / 1000.0) * input_device_config.sample_rate() as f32) as usize;
let output_samples_needed = ((20.0 / 1000.0)
* input_device_config.sample_rate() as f32
* force_channels as f32) as usize;
let samples_needed = frames_needed * input_device_config.channels() as usize;
let mut frame_buffer = vec![0f32; samples_needed];
let mut output_frame_buffer = vec![0f32; output_samples_needed];
let mut resampler_input_buffer = vec![0.0f32; frames_needed * force_channels as usize];
let mut resampler = Async::<f32>::new_poly(
48000.0 / 44100.0,
1.1,
PolynomialDegree::Cubic,
frames_needed,
force_channels as usize,
FixedAsync::Output,
)
.unwrap();
let mut resampler_output_buffer =
vec![0.0f32; resampler.output_frames_max() * force_channels as usize];
let encoder_channels: ropus::Channels = match force_channels {
1 => ropus::Channels::Mono,
@@ -338,13 +351,10 @@ pub async fn audio_stream_spawner(
}
};
let mut opus_encoder = Encoder::builder(
input_device_config.sample_rate(),
encoder_channels,
ropus::Application::Voip,
)
.build()
.unwrap();
let mut opus_encoder =
Encoder::builder(SAMPLE_RATE_48K, encoder_channels, ropus::Application::Voip)
.build()
.unwrap();
let mut encoded_frame_buffer = [0u8; 2048];
@@ -352,26 +362,40 @@ pub async fn audio_stream_spawner(
if consumer.occupied_len() >= samples_needed {
consumer.pop_slice(&mut frame_buffer);
if force_channels == input_device_config.channels() {
output_frame_buffer.copy_from_slice(frame_buffer.as_slice());
} else if force_channels < input_device_config.channels() {
let div = (input_device_config.channels() / force_channels) as usize;
let frame_buffer_adapter = InterleavedSlice::new(
&frame_buffer,
input_device_config.channels() as usize,
frames_needed,
)
.unwrap();
output_frame_buffer.fill(0.0);
frame_buffer_adapter.copy_from_channel_to_slice(0, 0, &mut resampler_input_buffer); // im only using first channel for now
for (i, input_sample) in frame_buffer.iter().enumerate() {
output_frame_buffer[i / div] += *input_sample / div as f32;
}
} else {
let div = (force_channels / input_device_config.channels()) as usize;
let resampler_input = InterleavedSlice::new(
&resampler_input_buffer,
force_channels as usize,
frames_needed,
)
.unwrap();
for (i, output_sample) in output_frame_buffer.iter_mut().enumerate() {
*output_sample = frame_buffer[i / div];
}
}
let mut resampler_output = InterleavedSlice::new_mut(
&mut resampler_output_buffer,
force_channels as usize,
resampler.output_frames_max(),
)
.unwrap();
let (_frames_read, frames_written) = resampler
.process_into_buffer(&resampler_input, &mut resampler_output, None)
.unwrap();
let valid_samples = frames_written * force_channels as usize;
let encoded_len = opus_encoder
.encode_float(&output_frame_buffer.as_slice(), &mut encoded_frame_buffer)
.encode_float(
&resampler_output_buffer[..valid_samples],
&mut encoded_frame_buffer,
)
.expect("failed to encode frame");
let mut payload = Vec::<u8>::with_capacity(4 + 1 + encoded_len * 4);
+31 -79
View File
@@ -123,46 +123,14 @@ impl DeviceSelectScreen {
if let Some(default_input_device) = host.default_input_device() {
if let Ok(default_config) = default_input_device.default_input_config() {
if default_config.sample_rate() == SAMPLE_RATE_48K
&& (default_config.channels() == 1)
{
input_devices.push((default_input_device.clone(), default_config));
}
} else if let Ok(supported_configs) = default_input_device.supported_input_configs() {
for supported_config in supported_configs {
if let Some(input_config) =
supported_config.try_with_sample_rate(SAMPLE_RATE_48K)
{
if input_config.channels() == 1 {
input_devices.push((default_input_device, input_config));
break;
}
}
}
input_devices.push((default_input_device.clone(), default_config));
}
}
if let Ok(possible_input_devices) = host.input_devices() {
for possible_input_device in possible_input_devices {
if let Ok(default_config) = possible_input_device.default_input_config() {
if default_config.sample_rate() == SAMPLE_RATE_48K
&& (default_config.channels() == 1)
{
input_devices.push((possible_input_device.clone(), default_config));
continue;
}
}
if let Ok(supported_configs) = possible_input_device.supported_input_configs() {
for supported_config in supported_configs {
if let Some(input_config) =
supported_config.try_with_sample_rate(SAMPLE_RATE_48K)
{
if input_config.channels() == 1 {
input_devices.push((possible_input_device, input_config));
break;
}
}
}
input_devices.push((possible_input_device.clone(), default_config));
}
}
}
@@ -171,47 +139,14 @@ impl DeviceSelectScreen {
if let Some(default_output_device) = host.default_output_device() {
if let Ok(default_config) = default_output_device.default_output_config() {
if default_config.sample_rate() == SAMPLE_RATE_48K
&& (default_config.channels() == 1 || default_config.channels() == 2)
{
output_devices.push((default_output_device.clone(), default_config.into()));
}
} else if let Ok(supported_configs) = default_output_device.supported_output_configs() {
for supported_config in supported_configs {
if let Some(output_config) =
supported_config.try_with_sample_rate(SAMPLE_RATE_48K)
{
if output_config.channels() == 1 || output_config.channels() == 2 {
output_devices.push((default_output_device, output_config.into()));
break;
}
}
}
output_devices.push((default_output_device.clone(), default_config.into()));
}
}
if let Ok(possible_output_devices) = host.output_devices() {
for possible_output_device in possible_output_devices {
if let Ok(default_config) = possible_output_device.default_output_config() {
if default_config.sample_rate() == SAMPLE_RATE_48K
&& (default_config.channels() == 1 || default_config.channels() == 2)
{
output_devices
.push((possible_output_device.clone(), default_config.into()));
continue;
}
}
if let Ok(supported_configs) = possible_output_device.supported_output_configs() {
for supported_config in supported_configs {
if let Some(output_config) =
supported_config.try_with_sample_rate(SAMPLE_RATE_48K)
{
if output_config.channels() == 1 || output_config.channels() == 2 {
output_devices.push((possible_output_device, output_config.into()));
break;
}
}
}
output_devices.push((possible_output_device.clone(), default_config.into()));
}
}
}
@@ -261,11 +196,18 @@ impl DeviceSelectScreen {
let widths = [Constraint::Percentage(100), Constraint::Min(8)];
let mut input_device_rows: Vec<Row> = Vec::new();
for (device, config) in self.input_devices.iter() {
input_device_rows.push(Row::new(vec![
device.description().unwrap().name().to_string(),
config.channels().to_string(),
]));
for (i, (device, config)) in self.input_devices.iter().enumerate() {
if i == 0 {
input_device_rows.push(Row::new(vec![
format!("(default) {}", device.description().unwrap().name()),
config.channels().to_string(),
]));
} else {
input_device_rows.push(Row::new(vec![
device.description().unwrap().name().to_string(),
config.channels().to_string(),
]));
}
}
let input_device_table = Table::new(input_device_rows, widths)
.header(
@@ -296,11 +238,21 @@ impl DeviceSelectScreen {
frame.render_stateful_widget(input_device_table, left_block, &mut self.input_device_state);
let mut output_device_rows: Vec<Row> = Vec::new();
for (device, config) in self.output_devices.iter() {
output_device_rows.push(Row::new(vec![
device.description().unwrap().name().to_string(),
config.channels().to_string(),
]));
for (i, (device, config)) in self.output_devices.iter().enumerate() {
if i == 0 {
output_device_rows.push(Row::new(vec![
format!(
"(default) {}",
device.description().unwrap().name().to_string()
),
config.channels().to_string(),
]));
} else {
output_device_rows.push(Row::new(vec![
device.description().unwrap().name().to_string(),
config.channels().to_string(),
]));
}
}
let output_device_table = Table::new(output_device_rows, widths)
.header(