1use std::{
2 str::FromStr,
3 sync::atomic::{AtomicBool, Ordering},
4};
5
6use cpal::DeviceId;
7use gpui::App;
8use settings::{RegisterSetting, Settings, SettingsStore};
9
10#[derive(Clone, Debug, RegisterSetting)]
11pub struct AudioSettings {
12 /// Opt into the new audio system.
13 ///
14 /// You need to rejoin a call for this setting to apply
15 pub rodio_audio: bool, // default is false
16 /// Requires 'rodio_audio: true'
17 ///
18 /// Automatically increase or decrease you microphone's volume. This affects how
19 /// loud you sound to others.
20 ///
21 /// Recommended: off (default)
22 /// Microphones are too quite in zed, until everyone is on experimental
23 /// audio and has auto speaker volume on this will make you very loud
24 /// compared to other speakers.
25 pub auto_microphone_volume: bool,
26 /// Requires 'rodio_audio: true'
27 ///
28 /// Automatically increate or decrease the volume of other call members.
29 /// This only affects how things sound for you.
30 pub auto_speaker_volume: bool,
31 /// Requires 'rodio_audio: true'
32 ///
33 /// Remove background noises. Works great for typing, cars, dogs, AC. Does
34 /// not work well on music.
35 pub denoise: bool,
36 /// Requires 'rodio_audio: true'
37 ///
38 /// Use audio parameters compatible with the previous versions of
39 /// experimental audio and non-experimental audio. When this is false you
40 /// will sound strange to anyone not on the latest experimental audio. In
41 /// the future we will migrate by setting this to false
42 ///
43 /// You need to rejoin a call for this setting to apply
44 pub legacy_audio_compatible: bool,
45 /// Requires 'rodio_audio: true'
46 ///
47 /// Select specific output audio device.
48 pub output_audio_device: Option<DeviceId>,
49 /// Requires 'rodio_audio: true'
50 ///
51 /// Select specific input audio device.
52 pub input_audio_device: Option<DeviceId>,
53}
54
55/// Configuration of audio in Zed
56impl Settings for AudioSettings {
57 fn from_settings(content: &settings::SettingsContent) -> Self {
58 let audio = &content.audio.as_ref().unwrap();
59 AudioSettings {
60 rodio_audio: audio.rodio_audio.unwrap(),
61 auto_microphone_volume: audio.auto_microphone_volume.unwrap(),
62 auto_speaker_volume: audio.auto_speaker_volume.unwrap(),
63 denoise: audio.denoise.unwrap(),
64 legacy_audio_compatible: audio.legacy_audio_compatible.unwrap(),
65 output_audio_device: audio
66 .output_audio_device
67 .as_ref()
68 .and_then(|x| x.0.as_ref().and_then(|id| DeviceId::from_str(&id).ok())),
69 input_audio_device: audio
70 .input_audio_device
71 .as_ref()
72 .and_then(|x| x.0.as_ref().and_then(|id| DeviceId::from_str(&id).ok())),
73 }
74 }
75}
76
77/// See docs on [LIVE_SETTINGS]
78pub(crate) struct LiveSettings {
79 pub(crate) auto_microphone_volume: AtomicBool,
80 pub(crate) auto_speaker_volume: AtomicBool,
81 pub(crate) denoise: AtomicBool,
82}
83
84impl LiveSettings {
85 pub(crate) fn initialize(&self, cx: &mut App) {
86 cx.observe_global::<SettingsStore>(move |cx| {
87 LIVE_SETTINGS.auto_microphone_volume.store(
88 AudioSettings::get_global(cx).auto_microphone_volume,
89 Ordering::Relaxed,
90 );
91 LIVE_SETTINGS.auto_speaker_volume.store(
92 AudioSettings::get_global(cx).auto_speaker_volume,
93 Ordering::Relaxed,
94 );
95
96 let denoise_enabled = AudioSettings::get_global(cx).denoise;
97 #[cfg(debug_assertions)]
98 {
99 static DENOISE_WARNING_SEND: AtomicBool = AtomicBool::new(false);
100 if denoise_enabled && !DENOISE_WARNING_SEND.load(Ordering::Relaxed) {
101 DENOISE_WARNING_SEND.store(true, Ordering::Relaxed);
102 log::warn!("Denoise does not work on debug builds, not enabling")
103 }
104 }
105 #[cfg(not(debug_assertions))]
106 LIVE_SETTINGS
107 .denoise
108 .store(denoise_enabled, Ordering::Relaxed);
109 })
110 .detach();
111
112 let init_settings = AudioSettings::get_global(cx);
113 LIVE_SETTINGS
114 .auto_microphone_volume
115 .store(init_settings.auto_microphone_volume, Ordering::Relaxed);
116 LIVE_SETTINGS
117 .auto_speaker_volume
118 .store(init_settings.auto_speaker_volume, Ordering::Relaxed);
119 let denoise_enabled = AudioSettings::get_global(cx).denoise;
120 #[cfg(debug_assertions)]
121 if denoise_enabled {
122 log::warn!("Denoise does not work on debug builds, not enabling")
123 }
124 #[cfg(not(debug_assertions))]
125 LIVE_SETTINGS
126 .denoise
127 .store(denoise_enabled, Ordering::Relaxed);
128 }
129}
130
131/// Allows access to settings from the audio thread. Updated by
132/// observer of SettingsStore. Needed because audio playback and recording are
133/// real time and must each run in a dedicated OS thread, therefore we can not
134/// use the background executor.
135pub(crate) static LIVE_SETTINGS: LiveSettings = LiveSettings {
136 auto_microphone_volume: AtomicBool::new(true),
137 auto_speaker_volume: AtomicBool::new(true),
138 denoise: AtomicBool::new(true),
139};