Add first-pass sound support to Zed

This commit is contained in:
Mikayla Maki 2023-07-03 13:30:04 -07:00
parent 138de37cbf
commit d2127825e3
No known key found for this signature in database
17 changed files with 213 additions and 79 deletions

23
crates/audio/Cargo.toml Normal file
View file

@ -0,0 +1,23 @@
[package]
name = "audio"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/audio.rs"
doctest = false
[dependencies]
gpui = { path = "../gpui" }
collections = { path = "../collections" }
util = { path = "../util" }
rodio = "0.17.1"
log.workspace = true
anyhow.workspace = true
parking_lot.workspace = true
[dev-dependencies]

View file

@ -0,0 +1,44 @@
use std::{io::Cursor, sync::Arc};
use anyhow::Result;
use collections::HashMap;
use gpui::{AppContext, AssetSource};
use rodio::{
source::{Buffered, SamplesConverter},
Decoder, Source,
};
type Sound = Buffered<SamplesConverter<Decoder<Cursor<Vec<u8>>>, f32>>;
pub struct SoundRegistry {
cache: Arc<parking_lot::Mutex<HashMap<String, Sound>>>,
assets: Box<dyn AssetSource>,
}
impl SoundRegistry {
pub fn new(source: impl AssetSource) -> Arc<Self> {
Arc::new(Self {
cache: Default::default(),
assets: Box::new(source),
})
}
pub fn global(cx: &AppContext) -> Arc<Self> {
cx.global::<Arc<Self>>().clone()
}
pub fn get(&self, name: &str) -> Result<impl Source<Item = f32>> {
if let Some(wav) = self.cache.lock().get(name) {
return Ok(wav.clone());
}
let path = format!("sounds/{}.wav", name);
let bytes = self.assets.load(&path)?.into_owned();
let cursor = Cursor::new(bytes);
let source = Decoder::new(cursor)?.convert_samples::<f32>().buffered();
self.cache.lock().insert(name.to_string(), source.clone());
Ok(source)
}
}

59
crates/audio/src/audio.rs Normal file
View file

@ -0,0 +1,59 @@
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,
}
impl Sound {
fn file(&self) -> &'static str {
match self {
Self::Joined => "joined",
Self::Leave => "leave",
Self::Mute => "mute",
Self::Unmute => "unmute",
}
}
}
pub struct Audio {
_output_stream: Option<OutputStream>,
output_handle: Option<OutputStreamHandle>,
}
impl Audio {
pub fn new() -> Self {
let (_output_stream, output_handle) = OutputStream::try_default().log_err().unzip();
Self {
_output_stream,
output_handle,
}
}
pub fn play_sound(sound: Sound, cx: &AppContext) {
let this = cx.global::<Self>();
let Some(output_handle) = this.output_handle.as_ref() else {
return;
};
let Some(source) = SoundRegistry::global(cx).get(sound.file()).log_err() else {
return;
};
output_handle.play_raw(source).log_err();
}
}