@@ -188,12 +188,15 @@ impl AudioStack {
|
||||
let voip_parts = audio::VoipParts::new(cx)?;
|
||||
// Audio needs to run real-time and should never be paused. That is why we are using a
|
||||
// normal std::thread and not a background task
|
||||
thread::spawn(move || {
|
||||
// microphone is non send on mac
|
||||
let microphone = audio::Audio::open_microphone(voip_parts)?;
|
||||
send_to_livekit(frame_tx, microphone);
|
||||
Ok::<(), anyhow::Error>(())
|
||||
});
|
||||
thread::Builder::new()
|
||||
.name("AudioCapture".to_string())
|
||||
.spawn(move || {
|
||||
// microphone is non send on mac
|
||||
let microphone = audio::Audio::open_microphone(voip_parts)?;
|
||||
send_to_livekit(frame_tx, microphone);
|
||||
Ok::<(), anyhow::Error>(())
|
||||
})
|
||||
.unwrap();
|
||||
Task::ready(Ok(()))
|
||||
} else {
|
||||
self.executor.spawn(async move {
|
||||
@@ -229,57 +232,60 @@ impl AudioStack {
|
||||
let mut resampler = audio_resampler::AudioResampler::default();
|
||||
let mut buf = Vec::new();
|
||||
|
||||
thread::spawn(move || {
|
||||
let output_stream = output_device.build_output_stream(
|
||||
&output_config.config(),
|
||||
{
|
||||
move |mut data, _info| {
|
||||
while data.len() > 0 {
|
||||
if data.len() <= buf.len() {
|
||||
let rest = buf.split_off(data.len());
|
||||
data.copy_from_slice(&buf);
|
||||
buf = rest;
|
||||
return;
|
||||
}
|
||||
if buf.len() > 0 {
|
||||
let (prefix, suffix) = data.split_at_mut(buf.len());
|
||||
prefix.copy_from_slice(&buf);
|
||||
data = suffix;
|
||||
}
|
||||
thread::Builder::new()
|
||||
.name("AudioPlayback".to_owned())
|
||||
.spawn(move || {
|
||||
let output_stream = output_device.build_output_stream(
|
||||
&output_config.config(),
|
||||
{
|
||||
move |mut data, _info| {
|
||||
while data.len() > 0 {
|
||||
if data.len() <= buf.len() {
|
||||
let rest = buf.split_off(data.len());
|
||||
data.copy_from_slice(&buf);
|
||||
buf = rest;
|
||||
return;
|
||||
}
|
||||
if buf.len() > 0 {
|
||||
let (prefix, suffix) = data.split_at_mut(buf.len());
|
||||
prefix.copy_from_slice(&buf);
|
||||
data = suffix;
|
||||
}
|
||||
|
||||
let mut mixer = mixer.lock();
|
||||
let mixed = mixer.mix(output_config.channels() as usize);
|
||||
let sampled = resampler.remix_and_resample(
|
||||
mixed,
|
||||
sample_rate / 100,
|
||||
num_channels,
|
||||
sample_rate,
|
||||
output_config.channels() as u32,
|
||||
output_config.sample_rate().0,
|
||||
);
|
||||
buf = sampled.to_vec();
|
||||
apm.lock()
|
||||
.process_reverse_stream(
|
||||
&mut buf,
|
||||
output_config.sample_rate().0 as i32,
|
||||
output_config.channels() as i32,
|
||||
)
|
||||
.ok();
|
||||
let mut mixer = mixer.lock();
|
||||
let mixed = mixer.mix(output_config.channels() as usize);
|
||||
let sampled = resampler.remix_and_resample(
|
||||
mixed,
|
||||
sample_rate / 100,
|
||||
num_channels,
|
||||
sample_rate,
|
||||
output_config.channels() as u32,
|
||||
output_config.sample_rate().0,
|
||||
);
|
||||
buf = sampled.to_vec();
|
||||
apm.lock()
|
||||
.process_reverse_stream(
|
||||
&mut buf,
|
||||
output_config.sample_rate().0 as i32,
|
||||
output_config.channels() as i32,
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|error| log::error!("error playing audio track: {:?}", error),
|
||||
Some(Duration::from_millis(100)),
|
||||
);
|
||||
},
|
||||
|error| log::error!("error playing audio track: {:?}", error),
|
||||
Some(Duration::from_millis(100)),
|
||||
);
|
||||
|
||||
let Some(output_stream) = output_stream.log_err() else {
|
||||
return;
|
||||
};
|
||||
let Some(output_stream) = output_stream.log_err() else {
|
||||
return;
|
||||
};
|
||||
|
||||
output_stream.play().log_err();
|
||||
// Block forever to keep the output stream alive
|
||||
end_on_drop_rx.recv().ok();
|
||||
});
|
||||
output_stream.play().log_err();
|
||||
// Block forever to keep the output stream alive
|
||||
end_on_drop_rx.recv().ok();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
device_change_listener.next().await;
|
||||
drop(end_on_drop_tx)
|
||||
@@ -300,77 +306,81 @@ impl AudioStack {
|
||||
let frame_tx = frame_tx.clone();
|
||||
let mut resampler = audio_resampler::AudioResampler::default();
|
||||
|
||||
thread::spawn(move || {
|
||||
maybe!({
|
||||
if let Some(name) = device.name().ok() {
|
||||
log::info!("Using microphone: {}", name)
|
||||
} else {
|
||||
log::info!("Using microphone: <unknown>");
|
||||
}
|
||||
thread::Builder::new()
|
||||
.name("AudioCapture".to_owned())
|
||||
.spawn(move || {
|
||||
maybe!({
|
||||
if let Some(name) = device.name().ok() {
|
||||
log::info!("Using microphone: {}", name)
|
||||
} else {
|
||||
log::info!("Using microphone: <unknown>");
|
||||
}
|
||||
|
||||
let ten_ms_buffer_size =
|
||||
(config.channels() as u32 * config.sample_rate().0 / 100) as usize;
|
||||
let mut buf: Vec<i16> = Vec::with_capacity(ten_ms_buffer_size);
|
||||
let ten_ms_buffer_size =
|
||||
(config.channels() as u32 * config.sample_rate().0 / 100) as usize;
|
||||
let mut buf: Vec<i16> = Vec::with_capacity(ten_ms_buffer_size);
|
||||
|
||||
let stream = device
|
||||
.build_input_stream_raw(
|
||||
&config.config(),
|
||||
config.sample_format(),
|
||||
move |data, _: &_| {
|
||||
let data =
|
||||
crate::get_sample_data(config.sample_format(), data).log_err();
|
||||
let Some(data) = data else {
|
||||
return;
|
||||
};
|
||||
let mut data = data.as_slice();
|
||||
let stream = device
|
||||
.build_input_stream_raw(
|
||||
&config.config(),
|
||||
config.sample_format(),
|
||||
move |data, _: &_| {
|
||||
let data = crate::get_sample_data(config.sample_format(), data)
|
||||
.log_err();
|
||||
let Some(data) = data else {
|
||||
return;
|
||||
};
|
||||
let mut data = data.as_slice();
|
||||
|
||||
while data.len() > 0 {
|
||||
let remainder = (buf.capacity() - buf.len()).min(data.len());
|
||||
buf.extend_from_slice(&data[..remainder]);
|
||||
data = &data[remainder..];
|
||||
while data.len() > 0 {
|
||||
let remainder =
|
||||
(buf.capacity() - buf.len()).min(data.len());
|
||||
buf.extend_from_slice(&data[..remainder]);
|
||||
data = &data[remainder..];
|
||||
|
||||
if buf.capacity() == buf.len() {
|
||||
let mut sampled = resampler
|
||||
.remix_and_resample(
|
||||
buf.as_slice(),
|
||||
config.sample_rate().0 / 100,
|
||||
config.channels() as u32,
|
||||
config.sample_rate().0,
|
||||
num_channels,
|
||||
sample_rate,
|
||||
)
|
||||
.to_owned();
|
||||
apm.lock()
|
||||
.process_stream(
|
||||
&mut sampled,
|
||||
sample_rate as i32,
|
||||
num_channels as i32,
|
||||
)
|
||||
.log_err();
|
||||
buf.clear();
|
||||
frame_tx
|
||||
.unbounded_send(AudioFrame {
|
||||
data: Cow::Owned(sampled),
|
||||
sample_rate,
|
||||
num_channels,
|
||||
samples_per_channel: sample_rate / 100,
|
||||
})
|
||||
.ok();
|
||||
if buf.capacity() == buf.len() {
|
||||
let mut sampled = resampler
|
||||
.remix_and_resample(
|
||||
buf.as_slice(),
|
||||
config.sample_rate().0 / 100,
|
||||
config.channels() as u32,
|
||||
config.sample_rate().0,
|
||||
num_channels,
|
||||
sample_rate,
|
||||
)
|
||||
.to_owned();
|
||||
apm.lock()
|
||||
.process_stream(
|
||||
&mut sampled,
|
||||
sample_rate as i32,
|
||||
num_channels as i32,
|
||||
)
|
||||
.log_err();
|
||||
buf.clear();
|
||||
frame_tx
|
||||
.unbounded_send(AudioFrame {
|
||||
data: Cow::Owned(sampled),
|
||||
sample_rate,
|
||||
num_channels,
|
||||
samples_per_channel: sample_rate / 100,
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|err| log::error!("error capturing audio track: {:?}", err),
|
||||
Some(Duration::from_millis(100)),
|
||||
)
|
||||
.context("failed to build input stream")?;
|
||||
},
|
||||
|err| log::error!("error capturing audio track: {:?}", err),
|
||||
Some(Duration::from_millis(100)),
|
||||
)
|
||||
.context("failed to build input stream")?;
|
||||
|
||||
stream.play()?;
|
||||
// Keep the thread alive and holding onto the `stream`
|
||||
end_on_drop_rx.recv().ok();
|
||||
anyhow::Ok(Some(()))
|
||||
stream.play()?;
|
||||
// Keep the thread alive and holding onto the `stream`
|
||||
end_on_drop_rx.recv().ok();
|
||||
anyhow::Ok(Some(()))
|
||||
})
|
||||
.log_err();
|
||||
})
|
||||
.log_err();
|
||||
});
|
||||
.unwrap();
|
||||
|
||||
device_change_listener.next().await;
|
||||
drop(end_on_drop_tx)
|
||||
|
||||
Reference in New Issue
Block a user