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 /// Select specific output audio device.
46 pub output_audio_device: Option<DeviceId>,
47 /// Select specific input audio device.
48 pub input_audio_device: Option<DeviceId>,
49}
50
51/// Configuration of audio in Zed
52impl Settings for AudioSettings {
53 fn from_settings(content: &settings::SettingsContent) -> Self {
54 let audio = &content.audio.as_ref().unwrap();
55 AudioSettings {
56 rodio_audio: audio.rodio_audio.unwrap(),
57 auto_microphone_volume: audio.auto_microphone_volume.unwrap(),
58 auto_speaker_volume: audio.auto_speaker_volume.unwrap(),
59 denoise: audio.denoise.unwrap(),
60 legacy_audio_compatible: audio.legacy_audio_compatible.unwrap(),
61 output_audio_device: audio
62 .output_audio_device
63 .as_ref()
64 .and_then(|x| x.0.as_ref().and_then(|id| DeviceId::from_str(&id).ok())),
65 input_audio_device: audio
66 .input_audio_device
67 .as_ref()
68 .and_then(|x| x.0.as_ref().and_then(|id| DeviceId::from_str(&id).ok())),
69 }
70 }
71}
72
73/// See docs on [LIVE_SETTINGS]
74pub(crate) struct LiveSettings {
75 pub(crate) auto_microphone_volume: AtomicBool,
76 pub(crate) auto_speaker_volume: AtomicBool,
77 pub(crate) denoise: AtomicBool,
78}
79
80impl LiveSettings {
81 pub(crate) fn initialize(&self, cx: &mut App) {
82 cx.observe_global::<SettingsStore>(move |cx| {
83 LIVE_SETTINGS.auto_microphone_volume.store(
84 AudioSettings::get_global(cx).auto_microphone_volume,
85 Ordering::Relaxed,
86 );
87 LIVE_SETTINGS.auto_speaker_volume.store(
88 AudioSettings::get_global(cx).auto_speaker_volume,
89 Ordering::Relaxed,
90 );
91
92 let denoise_enabled = AudioSettings::get_global(cx).denoise;
93 #[cfg(debug_assertions)]
94 {
95 static DENOISE_WARNING_SEND: AtomicBool = AtomicBool::new(false);
96 if denoise_enabled && !DENOISE_WARNING_SEND.load(Ordering::Relaxed) {
97 DENOISE_WARNING_SEND.store(true, Ordering::Relaxed);
98 log::warn!("Denoise does not work on debug builds, not enabling")
99 }
100 }
101 #[cfg(not(debug_assertions))]
102 LIVE_SETTINGS
103 .denoise
104 .store(denoise_enabled, Ordering::Relaxed);
105 })
106 .detach();
107
108 let init_settings = AudioSettings::get_global(cx);
109 LIVE_SETTINGS
110 .auto_microphone_volume
111 .store(init_settings.auto_microphone_volume, Ordering::Relaxed);
112 LIVE_SETTINGS
113 .auto_speaker_volume
114 .store(init_settings.auto_speaker_volume, Ordering::Relaxed);
115 let denoise_enabled = AudioSettings::get_global(cx).denoise;
116 #[cfg(debug_assertions)]
117 if denoise_enabled {
118 log::warn!("Denoise does not work on debug builds, not enabling")
119 }
120 #[cfg(not(debug_assertions))]
121 LIVE_SETTINGS
122 .denoise
123 .store(denoise_enabled, Ordering::Relaxed);
124 }
125}
126
127/// Allows access to settings from the audio thread. Updated by
128/// observer of SettingsStore. Needed because audio playback and recording are
129/// real time and must each run in a dedicated OS thread, therefore we can not
130/// use the background executor.
131pub(crate) static LIVE_SETTINGS: LiveSettings = LiveSettings {
132 auto_microphone_volume: AtomicBool::new(true),
133 auto_speaker_volume: AtomicBool::new(true),
134 denoise: AtomicBool::new(true),
135};