converting microphone sample format to f32
This commit is contained in:
+61
-28
@@ -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
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user