Add automatic-volume to legacy (#51919)
## Context This allows using of Rodio's effects library within our home brewn audio pipeline. The alternative would be inlining Rodio's effects which is problematic from a legal stance. We would then have to make clear that code is not owned by zed-industries while the code would be surrounded by zed-industries owned code. This adaptor does incur a slight performance penalty (copying into a pre-allocated vec and back) however the impact will be immeasurably low. There is no latency impact. ## How to Review - Adds an adapter for Rodio effects - Enables the adapter and effects only when the setting is enabled -Makes the setting pub(crate) so we can use it from livekit playback ## Self-Review Checklist - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Added Automatic volume control to calls
This commit is contained in:
1 parent
f1cad55e44
commit
4caff7d747
3 files changed
+93
-3
No files matched your search
@@ -8,6 +8,7 @@ pub const CHANNEL_COUNT: ChannelCount = nz!(2);
|
||||
|
||||
mod audio_settings;
|
||||
pub use audio_settings::AudioSettings;
|
||||
pub use audio_settings::LIVE_SETTINGS;
|
||||
|
||||
mod audio_pipeline;
|
||||
pub use audio_pipeline::{Audio, VoipParts};
|
||||
|
||||
@@ -71,8 +71,8 @@ impl Settings for AudioSettings {
|
||||
}
|
||||
|
||||
/// See docs on [LIVE_SETTINGS]
|
||||
pub(crate) struct LiveSettings {
|
||||
pub(crate) auto_microphone_volume: AtomicBool,
|
||||
pub struct LiveSettings {
|
||||
pub auto_microphone_volume: AtomicBool,
|
||||
pub(crate) auto_speaker_volume: AtomicBool,
|
||||
pub(crate) denoise: AtomicBool,
|
||||
}
|
||||
@@ -128,7 +128,7 @@ impl LiveSettings {
|
||||
/// observer of SettingsStore. Needed because audio playback and recording are
|
||||
/// real time and must each run in a dedicated OS thread, therefore we can not
|
||||
/// use the background executor.
|
||||
pub(crate) static LIVE_SETTINGS: LiveSettings = LiveSettings {
|
||||
pub static LIVE_SETTINGS: LiveSettings = LiveSettings {
|
||||
auto_microphone_volume: AtomicBool::new(true),
|
||||
auto_speaker_volume: AtomicBool::new(true),
|
||||
denoise: AtomicBool::new(true),
|
||||
|
||||
@@ -23,6 +23,8 @@ use livekit::webrtc::{
|
||||
use log::info;
|
||||
use parking_lot::Mutex;
|
||||
use rodio::Source;
|
||||
use rodio::conversions::SampleTypeConverter;
|
||||
use rodio::source::{AutomaticGainControlSettings, LimitSettings};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::Settings;
|
||||
use std::cell::RefCell;
|
||||
@@ -380,6 +382,14 @@ impl AudioStack {
|
||||
let ten_ms_buffer_size =
|
||||
(config.channels() as u32 * config.sample_rate() / 100) as usize;
|
||||
let mut buf: Vec<i16> = Vec::with_capacity(ten_ms_buffer_size);
|
||||
let mut rodio_effects = RodioEffectsAdaptor::new(buf.len())
|
||||
.automatic_gain_control(AutomaticGainControlSettings {
|
||||
target_level: 0.50,
|
||||
attack_time: Duration::from_secs(1),
|
||||
release_time: Duration::from_secs(0),
|
||||
absolute_max_gain: 5.0,
|
||||
})
|
||||
.limit(LimitSettings::live_performance());
|
||||
|
||||
let stream = device
|
||||
.build_input_stream_raw(
|
||||
@@ -411,6 +421,21 @@ impl AudioStack {
|
||||
sample_rate,
|
||||
)
|
||||
.to_owned();
|
||||
|
||||
if audio::LIVE_SETTINGS
|
||||
.auto_microphone_volume
|
||||
.load(Ordering::Relaxed)
|
||||
{
|
||||
rodio_effects
|
||||
.inner_mut()
|
||||
.inner_mut()
|
||||
.fill_buffer_with(&sampled);
|
||||
sampled.clear();
|
||||
sampled.extend(SampleTypeConverter::<_, i16>::new(
|
||||
rodio_effects.by_ref(),
|
||||
));
|
||||
}
|
||||
|
||||
apm.lock()
|
||||
.process_stream(
|
||||
&mut sampled,
|
||||
@@ -419,6 +444,7 @@ impl AudioStack {
|
||||
)
|
||||
.log_err();
|
||||
buf.clear();
|
||||
|
||||
frame_tx
|
||||
.try_send(TimestampedFrame {
|
||||
frame: AudioFrame {
|
||||
@@ -453,6 +479,69 @@ impl AudioStack {
|
||||
}
|
||||
}
|
||||
|
||||
/// This allows using of Rodio's effects library within our home brewn audio
|
||||
/// pipeline. The alternative would be inlining Rodio's effects which is
|
||||
/// problematic from a legal stance. We would then have to make clear that code
|
||||
/// is not owned by zed-industries while the code would be surrounded by
|
||||
/// zed-industries owned code.
|
||||
///
|
||||
/// This adaptor does incur a slight performance penalty (copying into a
|
||||
/// pre-allocated vec and back) however the impact will be immeasurably low.
|
||||
///
|
||||
/// There is no latency impact.
|
||||
pub struct RodioEffectsAdaptor {
|
||||
input: Vec<rodio::Sample>,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl RodioEffectsAdaptor {
|
||||
// This implementation incorrect terminology confusing everyone. A normal
|
||||
// audio frame consists of all samples for one moment in time (one for mono,
|
||||
// two for stereo). Here a frame of audio refers to a 10ms buffer of samples.
|
||||
fn new(samples_per_frame: usize) -> Self {
|
||||
Self {
|
||||
input: Vec::with_capacity(samples_per_frame),
|
||||
pos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn fill_buffer_with(&mut self, integer_samples: &[i16]) {
|
||||
self.input.clear();
|
||||
self.input.extend(SampleTypeConverter::<_, f32>::new(
|
||||
integer_samples.iter().copied(),
|
||||
));
|
||||
self.pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for RodioEffectsAdaptor {
|
||||
type Item = rodio::Sample;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let sample = self.input.get(self.pos)?;
|
||||
self.pos += 1;
|
||||
Some(*sample)
|
||||
}
|
||||
}
|
||||
|
||||
impl rodio::Source for RodioEffectsAdaptor {
|
||||
fn current_span_len(&self) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
|
||||
fn channels(&self) -> rodio::ChannelCount {
|
||||
rodio::nz!(2)
|
||||
}
|
||||
|
||||
fn sample_rate(&self) -> rodio::SampleRate {
|
||||
rodio::nz!(48000)
|
||||
}
|
||||
|
||||
fn total_duration(&self) -> Option<Duration> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Speaker {
|
||||
pub name: String,
|
||||
|
||||
Reference in new issue
Block a user