assets.rs

 1use std::{io::Cursor, sync::Arc};
 2
 3use anyhow::{Context as _, Result};
 4use collections::HashMap;
 5use gpui::{App, AssetSource, Global};
 6use rodio::{Decoder, Source, source::Buffered};
 7
 8type Sound = Buffered<Decoder<Cursor<Vec<u8>>>>;
 9
10pub struct SoundRegistry {
11    cache: Arc<parking_lot::Mutex<HashMap<String, Sound>>>,
12    assets: Box<dyn AssetSource>,
13}
14
15struct GlobalSoundRegistry(Arc<SoundRegistry>);
16
17impl Global for GlobalSoundRegistry {}
18
19impl SoundRegistry {
20    pub fn new(source: impl AssetSource) -> Arc<Self> {
21        Arc::new(Self {
22            cache: Default::default(),
23            assets: Box::new(source),
24        })
25    }
26
27    pub fn global(cx: &App) -> Arc<Self> {
28        cx.global::<GlobalSoundRegistry>().0.clone()
29    }
30
31    pub(crate) fn set_global(source: impl AssetSource, cx: &mut App) {
32        cx.set_global(GlobalSoundRegistry(SoundRegistry::new(source)));
33    }
34
35    pub fn get(&self, name: &str) -> Result<impl Source<Item = f32> + use<>> {
36        if let Some(wav) = self.cache.lock().get(name) {
37            return Ok(wav.clone());
38        }
39
40        let path = format!("sounds/{}.wav", name);
41        let bytes = self
42            .assets
43            .load(&path)?
44            .map(anyhow::Ok)
45            .with_context(|| format!("No asset available for path {path}"))??
46            .into_owned();
47        let cursor = Cursor::new(bytes);
48        let source = Decoder::new(cursor)?.buffered();
49
50        self.cache.lock().insert(name.to_string(), source.clone());
51
52        Ok(source)
53    }
54}