converting microphone sample format to f32

This commit is contained in:
2026-09-15 16:24:34 +02:00
parent 5efd656822
commit c6b15e726b
2 changed files with 74 additions and 47 deletions
+61 -28
View File
@@ -1,10 +1,12 @@
use cpal::{ use cpal::{
Device, StreamConfig, Device, FromSample, SampleFormat, SizedSample, StreamConfig, SupportedStreamConfig,
traits::{DeviceTrait, StreamTrait}, traits::{DeviceTrait, StreamTrait},
}; };
use ringbuf::{ use ringbuf::{
HeapRb, HeapRb, SharedRb,
storage::Heap,
traits::{Consumer, Observer, Producer, Split}, traits::{Consumer, Observer, Producer, Split},
wrap::caching::Caching,
}; };
use ropus::Encoder; use ropus::Encoder;
use shared::AudioStreamInfo; use shared::AudioStreamInfo;
@@ -21,7 +23,7 @@ use crate::models::{
pub async fn audio_stream_receiver( pub async fn audio_stream_receiver(
output_device: Device, output_device: Device,
output_device_config: StreamConfig, output_device_config: SupportedStreamConfig,
active_input_audio_streams_producers: InputAudioStreamsProducers, active_input_audio_streams_producers: InputAudioStreamsProducers,
active_input_audio_streams_consumers: InputAudioStreamsConsumers, active_input_audio_streams_consumers: InputAudioStreamsConsumers,
active_input_audio_streams_encoders: InputAudioStreamsEncoders, active_input_audio_streams_encoders: InputAudioStreamsEncoders,
@@ -46,7 +48,7 @@ pub async fn audio_stream_receiver(
let err_fn = |err| eprintln!("stream error: {}", err); let err_fn = |err| eprintln!("stream error: {}", err);
let output_stream = output_device let output_stream = output_device
.build_output_stream(output_device_config, output_data_fn, err_fn, None) .build_output_stream(output_device_config.into(), output_data_fn, err_fn, None)
.expect("building output stream failed"); .expect("building output stream failed");
output_stream.play().expect("failed to play output stream"); output_stream.play().expect("failed to play output stream");
@@ -102,11 +104,11 @@ pub async fn audio_stream_receiver(
let mut active_streams_map = active_input_audio_streams_producers.lock().unwrap(); let mut active_streams_map = active_input_audio_streams_producers.lock().unwrap();
if let Some(producer) = active_streams_map.get_mut(&(client_id, stream_id)) { if let Some(producer) = active_streams_map.get_mut(&(client_id, stream_id)) {
if stream_info.channels == output_device_config.channels { if stream_info.channels == output_device_config.channels() {
producer.push_slice( producer.push_slice(
&decoded_samples[..decoded_len * stream_info.channels as usize], &decoded_samples[..decoded_len * stream_info.channels as usize],
); );
} else if stream_info.channels > output_device_config.channels { } else if stream_info.channels > output_device_config.channels() {
for (i, frame) in decoded_samples for (i, frame) in decoded_samples
[..decoded_len * stream_info.channels as usize] [..decoded_len * stream_info.channels as usize]
.chunks_exact(2) .chunks_exact(2)
@@ -121,7 +123,7 @@ pub async fn audio_stream_receiver(
); );
} else { } else {
for (i, frame) in decoded_samples_converted for (i, frame) in decoded_samples_converted
[..decoded_len * output_device_config.channels as usize] [..decoded_len * output_device_config.channels() as usize]
.chunks_exact_mut(2) .chunks_exact_mut(2)
.enumerate() .enumerate()
{ {
@@ -131,7 +133,7 @@ pub async fn audio_stream_receiver(
producer.push_slice( producer.push_slice(
&decoded_samples_converted &decoded_samples_converted
[..decoded_len * output_device_config.channels as usize], [..decoded_len * output_device_config.channels() as usize],
); );
} }
} }
@@ -141,27 +143,50 @@ pub async fn audio_stream_receiver(
} }
} }
fn cpal_build_input_stream<T>(
input_device: &Device,
input_device_config: StreamConfig,
mut producer: Caching<Arc<SharedRb<Heap<f32>>>, true, false>,
) -> cpal::Stream
where
T: SizedSample,
f32: FromSample<T>,
{
let input_data_fn = move |data: &[T], _: &cpal::InputCallbackInfo| {
for &sample in data {
let f32_sample: f32 = sample.to_sample::<f32>();
if producer.try_push(f32_sample).is_err() {}
}
};
let err_fn = |err| eprintln!("stream error: {}", err);
return input_device
.build_input_stream(input_device_config, input_data_fn, err_fn, None)
.expect("building input stream failed");
}
pub async fn audio_stream_spawner( pub async fn audio_stream_spawner(
input_device: Device, input_device: Device,
input_device_config: StreamConfig, input_device_config: SupportedStreamConfig,
global_event_tx: Sender<ClientEvent>, global_event_tx: Sender<ClientEvent>,
mut global_event_rx: Receiver<ClientEvent>, mut global_event_rx: Receiver<ClientEvent>,
udp_socket: Arc<UdpSocket>, udp_socket: Arc<UdpSocket>,
clinet_info: Arc<RwLock<ClientInfo>>, client_info: Arc<RwLock<ClientInfo>>,
) { ) {
let current_stream_id: u8 = rand::random(); let current_stream_id: u8 = rand::random();
loop { loop {
let (connected, client_id) = { let (connected, client_id) = {
let info = clinet_info.read().unwrap(); let info = client_info.read().unwrap();
(info.connected, info.client_id) (info.connected, info.client_id)
}; };
if connected { if connected {
let _ = global_event_tx.send(ClientEvent::NewAudioStreamRequest(AudioStreamInfo { let _ = global_event_tx.send(ClientEvent::NewAudioStreamRequest(AudioStreamInfo {
client_id: client_id, client_id: client_id,
stream_id: current_stream_id, stream_id: current_stream_id,
channels: input_device_config.channels, channels: input_device_config.channels(),
sample_rate: input_device_config.sample_rate, sample_rate: input_device_config.sample_rate(),
})); }));
break; break;
} }
@@ -181,30 +206,38 @@ pub async fn audio_stream_spawner(
} }
let ring_buffer_ms = 80.0; let ring_buffer_ms = 80.0;
let ring_buffer_frames = (ring_buffer_ms / 1000.0) * input_device_config.sample_rate as f32; let ring_buffer_frames = (ring_buffer_ms / 1000.0) * input_device_config.sample_rate() as f32;
let ring_buffer_samples = ring_buffer_frames as usize * input_device_config.channels as usize; let ring_buffer_samples = ring_buffer_frames as usize * input_device_config.channels() as usize;
let ring_buffer_size = ring_buffer_samples; let ring_buffer_size = ring_buffer_samples;
let ring_buffer = HeapRb::<f32>::new(ring_buffer_size); let ring_buffer = HeapRb::<f32>::new(ring_buffer_size);
let (mut producer, mut consumer) = ring_buffer.split(); let (producer, mut consumer) = ring_buffer.split();
let input_data_fn = move |data: &[f32], _: &cpal::InputCallbackInfo| { let input_stream = match input_device_config.sample_format() {
for &sample in data { SampleFormat::F32 => {
if producer.try_push(sample).is_err() {} cpal_build_input_stream::<f32>(&input_device, input_device_config.into(), producer)
}
SampleFormat::I32 => {
cpal_build_input_stream::<i32>(&input_device, input_device_config.into(), producer)
}
SampleFormat::I16 => {
cpal_build_input_stream::<i16>(&input_device, input_device_config.into(), producer)
}
SampleFormat::U16 => {
cpal_build_input_stream::<u16>(&input_device, input_device_config.into(), producer)
}
_ => {
eprintln!("unsupported stream format :(");
return;
} }
}; };
let err_fn = |err| eprintln!("stream error: {}", err);
let input_stream = input_device
.build_input_stream(input_device_config, input_data_fn, err_fn, None)
.expect("building input stream failed");
input_stream.play().expect("failed to play input stream"); input_stream.play().expect("failed to play input stream");
let frames_needed = ((20.0 / 1000.0) * input_device_config.sample_rate as f32) as usize; let frames_needed = ((20.0 / 1000.0) * input_device_config.sample_rate() as f32) as usize;
let mut frame_buffer = vec![0.0; frames_needed]; let mut frame_buffer = vec![0.0; frames_needed];
let channels: ropus::Channels = { let channels: ropus::Channels = {
if input_device_config.channels == 2 { if input_device_config.channels() == 2 {
ropus::Channels::Stereo ropus::Channels::Stereo
} else { } else {
ropus::Channels::Mono ropus::Channels::Mono
@@ -212,7 +245,7 @@ pub async fn audio_stream_spawner(
}; };
let mut opus_encoder = Encoder::builder( let mut opus_encoder = Encoder::builder(
input_device_config.sample_rate, input_device_config.sample_rate(),
channels, channels,
ropus::Application::Voip, ropus::Application::Voip,
) )
@@ -228,7 +261,7 @@ pub async fn audio_stream_spawner(
let mut payload = Vec::with_capacity(4 + 1 + frames_needed * 4); let mut payload = Vec::with_capacity(4 + 1 + frames_needed * 4);
let (server_address, client_id) = { let (server_address, client_id) = {
let info = clinet_info.read().unwrap(); let info = client_info.read().unwrap();
(info.server_address.clone(), info.client_id) (info.server_address.clone(), info.client_id)
}; };
+13 -19
View File
@@ -1,5 +1,5 @@
use cpal::{ use cpal::{
Device, SAMPLE_RATE_48K, SampleFormat, StreamConfig, Device, SAMPLE_RATE_48K, SampleFormat, SupportedStreamConfig,
traits::{DeviceTrait, HostTrait}, traits::{DeviceTrait, HostTrait},
}; };
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind}; use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
@@ -100,8 +100,8 @@ struct DeviceSelectScreen {
exit_screen: TuiScreen, exit_screen: TuiScreen,
input_device_state: TableState, input_device_state: TableState,
output_device_state: TableState, output_device_state: TableState,
input_devices: Vec<(Device, StreamConfig)>, input_devices: Vec<(Device, SupportedStreamConfig)>,
output_devices: Vec<(Device, StreamConfig)>, output_devices: Vec<(Device, SupportedStreamConfig)>,
selected_selection: u8, selected_selection: u8,
audio_control: Arc<AudioControl>, audio_control: Arc<AudioControl>,
@@ -119,25 +119,22 @@ impl DeviceSelectScreen {
let host = cpal::default_host(); let host = cpal::default_host();
let mut input_devices: Vec<(Device, StreamConfig)> = Vec::new(); let mut input_devices: Vec<(Device, SupportedStreamConfig)> = Vec::new();
if let Some(default_input_device) = host.default_input_device() { if let Some(default_input_device) = host.default_input_device() {
if let Ok(default_config) = default_input_device.default_input_config() { if let Ok(default_config) = default_input_device.default_input_config() {
if default_config.sample_rate() == SAMPLE_RATE_48K if default_config.sample_rate() == SAMPLE_RATE_48K
&& default_config.sample_format() == SampleFormat::F32
&& (default_config.channels() == 1) && (default_config.channels() == 1)
{ {
input_devices.push((default_input_device.clone(), default_config.into())); input_devices.push((default_input_device.clone(), default_config));
} }
} else if let Ok(supported_configs) = default_input_device.supported_input_configs() { } else if let Ok(supported_configs) = default_input_device.supported_input_configs() {
for supported_config in supported_configs { for supported_config in supported_configs {
if let Some(input_config) = if let Some(input_config) =
supported_config.try_with_sample_rate(SAMPLE_RATE_48K) supported_config.try_with_sample_rate(SAMPLE_RATE_48K)
{ {
if input_config.sample_format() == SampleFormat::F32 if input_config.channels() == 1 {
&& (input_config.channels() == 1) input_devices.push((default_input_device, input_config));
{
input_devices.push((default_input_device, input_config.into()));
break; break;
} }
} }
@@ -149,10 +146,9 @@ impl DeviceSelectScreen {
for possible_input_device in possible_input_devices { for possible_input_device in possible_input_devices {
if let Ok(default_config) = possible_input_device.default_input_config() { if let Ok(default_config) = possible_input_device.default_input_config() {
if default_config.sample_rate() == SAMPLE_RATE_48K if default_config.sample_rate() == SAMPLE_RATE_48K
&& default_config.sample_format() == SampleFormat::F32
&& (default_config.channels() == 1) && (default_config.channels() == 1)
{ {
input_devices.push((possible_input_device.clone(), default_config.into())); input_devices.push((possible_input_device.clone(), default_config));
continue; continue;
} }
} }
@@ -161,10 +157,8 @@ impl DeviceSelectScreen {
if let Some(input_config) = if let Some(input_config) =
supported_config.try_with_sample_rate(SAMPLE_RATE_48K) supported_config.try_with_sample_rate(SAMPLE_RATE_48K)
{ {
if input_config.sample_format() == SampleFormat::F32 if input_config.channels() == 1 {
&& (input_config.channels() == 1) input_devices.push((possible_input_device, input_config));
{
input_devices.push((possible_input_device, input_config.into()));
break; break;
} }
} }
@@ -173,7 +167,7 @@ impl DeviceSelectScreen {
} }
} }
let mut output_devices: Vec<(Device, StreamConfig)> = Vec::new(); let mut output_devices: Vec<(Device, SupportedStreamConfig)> = Vec::new();
if let Some(default_output_device) = host.default_output_device() { if let Some(default_output_device) = host.default_output_device() {
if let Ok(default_config) = default_output_device.default_output_config() { if let Ok(default_config) = default_output_device.default_output_config() {
@@ -276,7 +270,7 @@ impl DeviceSelectScreen {
for (device, config) in self.input_devices.iter() { for (device, config) in self.input_devices.iter() {
input_device_rows.push(Row::new(vec![ input_device_rows.push(Row::new(vec![
device.description().unwrap().name().to_string(), device.description().unwrap().name().to_string(),
config.channels.to_string(), config.channels().to_string(),
])); ]));
} }
let input_device_table = Table::new(input_device_rows, widths) let input_device_table = Table::new(input_device_rows, widths)
@@ -311,7 +305,7 @@ impl DeviceSelectScreen {
for (device, config) in self.output_devices.iter() { for (device, config) in self.output_devices.iter() {
output_device_rows.push(Row::new(vec![ output_device_rows.push(Row::new(vec![
device.description().unwrap().name().to_string(), device.description().unwrap().name().to_string(),
config.channels.to_string(), config.channels().to_string(),
])); ]));
} }
let output_device_table = Table::new(output_device_rows, widths) let output_device_table = Table::new(output_device_rows, widths)