1use assets::SoundRegistry;
2use derive_more::{Deref, DerefMut};
3use gpui::{AppContext, AssetSource, BorrowAppContext, Global};
4use rodio::{OutputStream, OutputStreamHandle};
5use util::ResultExt;
6
7mod assets;
8
9pub fn init(source: impl AssetSource, cx: &mut AppContext) {
10 SoundRegistry::set_global(source, cx);
11 cx.set_global(GlobalAudio(Audio::new()));
12}
13
14pub enum Sound {
15 Joined,
16 Leave,
17 Mute,
18 Unmute,
19 StartScreenshare,
20 StopScreenshare,
21}
22
23impl Sound {
24 fn file(&self) -> &'static str {
25 match self {
26 Self::Joined => "joined_call",
27 Self::Leave => "leave_call",
28 Self::Mute => "mute",
29 Self::Unmute => "unmute",
30 Self::StartScreenshare => "start_screenshare",
31 Self::StopScreenshare => "stop_screenshare",
32 }
33 }
34}
35
36pub struct Audio {
37 _output_stream: Option<OutputStream>,
38 output_handle: Option<OutputStreamHandle>,
39}
40
41#[derive(Deref, DerefMut)]
42struct GlobalAudio(Audio);
43
44impl Global for GlobalAudio {}
45
46impl Audio {
47 pub fn new() -> Self {
48 Self {
49 _output_stream: None,
50 output_handle: None,
51 }
52 }
53
54 fn ensure_output_exists(&mut self) -> Option<&OutputStreamHandle> {
55 if self.output_handle.is_none() {
56 let (_output_stream, output_handle) = OutputStream::try_default().log_err().unzip();
57 self.output_handle = output_handle;
58 self._output_stream = _output_stream;
59 }
60
61 self.output_handle.as_ref()
62 }
63
64 pub fn play_sound(sound: Sound, cx: &mut AppContext) {
65 if !cx.has_global::<GlobalAudio>() {
66 return;
67 }
68
69 cx.update_global::<GlobalAudio, _>(|this, cx| {
70 let output_handle = this.ensure_output_exists()?;
71 let source = SoundRegistry::global(cx).get(sound.file()).log_err()?;
72 output_handle.play_raw(source).log_err()?;
73 Some(())
74 });
75 }
76
77 pub fn end_call(cx: &mut AppContext) {
78 if !cx.has_global::<GlobalAudio>() {
79 return;
80 }
81
82 cx.update_global::<GlobalAudio, _>(|this, _| {
83 this._output_stream.take();
84 this.output_handle.take();
85 });
86 }
87}