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

View file

@ -1,18 +1,16 @@
mod assets;
pub mod participant;
pub mod room;
use std::sync::Arc;
use anyhow::{anyhow, Result};
use assets::SoundRegistry;
use client::{proto, Client, TypedEnvelope, User, UserStore};
use collections::HashSet;
use futures::{future::Shared, FutureExt};
use postage::watch;
use gpui::{
AppContext, AssetSource, AsyncAppContext, Entity, ModelContext, ModelHandle, Subscription,
AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Subscription,
Task, WeakModelHandle,
};
use project::Project;
@ -23,10 +21,8 @@ pub use room::Room;
pub fn init(
client: Arc<Client>,
user_store: ModelHandle<UserStore>,
source: impl AssetSource,
cx: &mut AppContext,
) {
cx.set_global(SoundRegistry::new(source));
let active_call = cx.add_model(|cx| ActiveCall::new(client, user_store, cx));
cx.set_global(active_call);
}

View file

@ -1,9 +1,9 @@
use crate::{
assets::SoundRegistry,
participant::{LocalParticipant, ParticipantLocation, RemoteParticipant, RemoteVideoTrack},
IncomingCall,
};
use anyhow::{anyhow, Result};
use audio::{Audio, Sound};
use client::{
proto::{self, PeerId},
Client, TypedEnvelope, User, UserStore,
@ -19,30 +19,11 @@ use live_kit_client::{
};
use postage::stream::Stream;
use project::Project;
use rodio::{OutputStream, OutputStreamHandle, Source};
use std::{future::Future, mem, pin::Pin, sync::Arc, time::Duration};
use util::{post_inc, ResultExt, TryFutureExt};
pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
enum Sound {
Joined,
Leaved,
Mute,
Unmute,
}
impl Sound {
fn file(&self) -> &'static str {
match self {
Self::Joined => "joined",
Self::Leaved => "leave",
Self::Mute => "mute",
Self::Unmute => "unmute",
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Event {
ParticipantLocationChanged {
@ -68,8 +49,6 @@ pub enum Event {
pub struct Room {
id: u64,
live_kit: Option<LiveKitRoom>,
_sound_output_stream: Option<OutputStream>,
sound_output_handle: Option<OutputStreamHandle>,
status: RoomStatus,
shared_projects: HashSet<WeakModelHandle<Project>>,
joined_projects: HashSet<WeakModelHandle<Project>>,
@ -173,6 +152,7 @@ impl Room {
let connect = room.connect(&connection_info.server_url, &connection_info.token);
cx.spawn(|this, mut cx| async move {
connect.await?;
this.update(&mut cx, |this, cx| this.share_microphone(cx))
.await?;
@ -198,14 +178,11 @@ impl Room {
let maintain_connection =
cx.spawn_weak(|this, cx| Self::maintain_connection(this, client.clone(), cx).log_err());
let (sound_output_stream, sound_output_handle) =
OutputStream::try_default().log_err().unzip();
Audio::play_sound(Sound::Joined, cx);
Self {
id,
live_kit: live_kit_room,
_sound_output_stream: sound_output_stream,
sound_output_handle,
status: RoomStatus::Online,
shared_projects: Default::default(),
joined_projects: Default::default(),
@ -292,6 +269,7 @@ impl Room {
room.apply_room_update(room_proto, cx)?;
anyhow::Ok(())
})?;
Ok(room)
})
}
@ -333,6 +311,8 @@ impl Room {
}
}
Audio::play_sound(Sound::Leave, cx);
self.status = RoomStatus::Offline;
self.remote_participants.clear();
self.pending_participants.clear();
@ -937,18 +917,6 @@ impl Room {
})
}
fn play_sound(&self, sound: Sound, cx: &AppContext) {
let Some(output_handle) = self.sound_output_handle.as_ref() else {
return;
};
let Some(source) = SoundRegistry::global(cx).get(sound.file()) else {
return;
};
output_handle.play_raw(source.convert_samples()).log_err();
}
pub fn join_project(
&mut self,
id: u64,
@ -962,8 +930,6 @@ impl Room {
let project =
Project::remote(id, client, user_store, language_registry, fs, cx.clone()).await?;
cx.read(|cx| this.read(cx).play_sound(Sound::Joined, cx));
this.update(&mut cx, |this, cx| {
this.joined_projects.retain(|project| {
if let Some(project) = project.upgrade(cx) {
@ -1269,38 +1235,20 @@ impl Room {
})
})
}
fn set_mute(
live_kit: &mut LiveKitRoom,
should_mute: bool,
cx: &mut ModelContext<Self>,
) -> Result<Task<Result<()>>> {
if !should_mute {
// clear user muting state.
live_kit.muted_by_user = false;
}
match &mut live_kit.microphone_track {
LocalTrack::None => Err(anyhow!("microphone was not shared")),
LocalTrack::Pending { muted, .. } => {
*muted = should_mute;
cx.notify();
Ok(Task::Ready(Some(Ok(()))))
}
LocalTrack::Published {
track_publication,
muted,
} => {
*muted = should_mute;
cx.notify();
Ok(cx.background().spawn(track_publication.set_mute(*muted)))
}
}
}
pub fn toggle_mute(&mut self, cx: &mut ModelContext<Self>) -> Result<Task<Result<()>>> {
let should_mute = !self.is_muted();
if let Some(live_kit) = self.live_kit.as_mut() {
let ret = Self::set_mute(live_kit, should_mute, cx);
let (ret_task, old_muted) = live_kit.set_mute(should_mute, cx)?;
live_kit.muted_by_user = should_mute;
ret
if old_muted == true && live_kit.deafened == true {
if let Some(task) = self.toggle_deafen(cx).ok() {
task.detach();
}
}
Ok(ret_task)
} else {
Err(anyhow!("LiveKit not started"))
}
@ -1316,7 +1264,7 @@ impl Room {
// When deafening, mute user's mic as well.
// When undeafening, unmute user's mic unless it was manually muted prior to deafening.
if live_kit.deafened || !live_kit.muted_by_user {
mute_task = Some(Self::set_mute(live_kit, live_kit.deafened, cx)?);
mute_task = Some(live_kit.set_mute(live_kit.deafened, cx)?.0);
};
for participant in self.remote_participants.values() {
for track in live_kit
@ -1389,6 +1337,48 @@ struct LiveKitRoom {
_maintain_tracks: [Task<()>; 2],
}
impl LiveKitRoom {
fn set_mute(
self: &mut LiveKitRoom,
should_mute: bool,
cx: &mut ModelContext<Room>,
) -> Result<(Task<Result<()>>, bool)> {
if !should_mute {
// clear user muting state.
self.muted_by_user = false;
}
let (result, old_muted) = match &mut self.microphone_track {
LocalTrack::None => Err(anyhow!("microphone was not shared")),
LocalTrack::Pending { muted, .. } => {
let old_muted = *muted;
*muted = should_mute;
cx.notify();
Ok((Task::Ready(Some(Ok(()))), old_muted))
}
LocalTrack::Published {
track_publication,
muted,
} => {
let old_muted = *muted;
*muted = should_mute;
cx.notify();
Ok((cx.background().spawn(track_publication.set_mute(*muted)), old_muted))
}
}?;
if old_muted != should_mute {
if should_mute {
Audio::play_sound(Sound::Mute, cx);
} else {
Audio::play_sound(Sound::Unmute, cx);
}
}
Ok((result, old_muted))
}
}
enum LocalTrack {
None,
Pending {