audio2.rs

  1use assets::SoundRegistry;
  2use futures::{channel::mpsc, StreamExt};
  3use gpui2::{AppContext, AssetSource, Executor};
  4use rodio::{OutputStream, OutputStreamHandle};
  5use util::ResultExt;
  6
  7mod assets;
  8
  9pub fn init(source: impl AssetSource, cx: &mut AppContext) {
 10    cx.set_global(Audio::new(cx.executor()));
 11    cx.set_global(SoundRegistry::new(source));
 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    tx: mpsc::UnboundedSender<Box<dyn FnOnce(&mut AudioState) + Send>>,
 38}
 39
 40struct AudioState {
 41    _output_stream: Option<OutputStream>,
 42    output_handle: Option<OutputStreamHandle>,
 43}
 44
 45impl AudioState {
 46    fn ensure_output_exists(&mut self) -> Option<&OutputStreamHandle> {
 47        if self.output_handle.is_none() {
 48            let (_output_stream, output_handle) = OutputStream::try_default().log_err().unzip();
 49            self.output_handle = output_handle;
 50            self._output_stream = _output_stream;
 51        }
 52
 53        self.output_handle.as_ref()
 54    }
 55
 56    fn take(&mut self) {
 57        self._output_stream.take();
 58        self.output_handle.take();
 59    }
 60}
 61
 62impl Audio {
 63    pub fn new(executor: &Executor) -> Self {
 64        let (tx, mut rx) = mpsc::unbounded::<Box<dyn FnOnce(&mut AudioState) + Send>>();
 65        executor
 66            .spawn_on_main(|| async move {
 67                let mut audio = AudioState {
 68                    _output_stream: None,
 69                    output_handle: None,
 70                };
 71
 72                while let Some(f) = rx.next().await {
 73                    (f)(&mut audio);
 74                }
 75            })
 76            .detach();
 77
 78        Self { tx }
 79    }
 80
 81    pub fn play_sound(sound: Sound, cx: &mut AppContext) {
 82        if !cx.has_global::<Self>() {
 83            return;
 84        }
 85
 86        let Some(source) = SoundRegistry::global(cx).get(sound.file()).log_err() else {
 87            return;
 88        };
 89
 90        let this = cx.global::<Self>();
 91        this.tx
 92            .unbounded_send(Box::new(move |state| {
 93                if let Some(output_handle) = state.ensure_output_exists() {
 94                    output_handle.play_raw(source).log_err();
 95                }
 96            }))
 97            .ok();
 98    }
 99
100    pub fn end_call(cx: &AppContext) {
101        if !cx.has_global::<Self>() {
102            return;
103        }
104
105        let this = cx.global::<Self>();
106
107        this.tx
108            .unbounded_send(Box::new(move |state| state.take()))
109            .ok();
110    }
111}