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