1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use assets::SoundRegistry;
use gpui::{AppContext, AssetSource};
use rodio::{OutputStream, OutputStreamHandle};
use util::ResultExt;

mod assets;

pub fn init(source: impl AssetSource, cx: &mut AppContext) {
    cx.set_global(SoundRegistry::new(source));
    cx.set_global(Audio::new());
}

pub enum Sound {
    Joined,
    Leave,
    Mute,
    Unmute,
    StartScreenshare,
    StopScreenshare,
}

impl Sound {
    fn file(&self) -> &'static str {
        match self {
            Self::Joined => "joined_call",
            Self::Leave => "leave_call",
            Self::Mute => "mute",
            Self::Unmute => "unmute",
            Self::StartScreenshare => "start_screenshare",
            Self::StopScreenshare => "stop_screenshare",
        }
    }
}

pub struct Audio {
    _output_stream: Option<OutputStream>,
    output_handle: Option<OutputStreamHandle>,
}

impl Audio {
    pub fn new() -> Self {
        Self {
            _output_stream: None,
            output_handle: None,
        }
    }

    fn ensure_output_exists(&mut self) -> Option<&OutputStreamHandle> {
        if self.output_handle.is_none() {
            let (_output_stream, output_handle) = OutputStream::try_default().log_err().unzip();
            self.output_handle = output_handle;
            self._output_stream = _output_stream;
        }

        self.output_handle.as_ref()
    }

    pub fn play_sound(sound: Sound, cx: &mut AppContext) {
        if !cx.has_global::<Self>() {
            return;
        }

        cx.update_global::<Self, _>(|this, cx| {
            let output_handle = this.ensure_output_exists()?;
            let source = SoundRegistry::global(cx).get(sound.file()).log_err()?;
            output_handle.play_raw(source).log_err()?;
            Some(())
        });
    }

    pub fn end_call(cx: &mut AppContext) {
        if !cx.has_global::<Self>() {
            return;
        }

        cx.update_global::<Self, _>(|this, _| {
            this._output_stream.take();
            this.output_handle.take();
        });
    }
}