From d76c7e1ba8bd23e873cb7ba09f190e215911e59d Mon Sep 17 00:00:00 2001 From: David Kleingeld Date: Mon, 25 Aug 2025 13:00:46 +0200 Subject: [PATCH 1/7] add first draft of glossary --- GLOSSARY.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 GLOSSARY.md diff --git a/GLOSSARY.md b/GLOSSARY.md new file mode 100644 index 0000000000..521fe53222 --- /dev/null +++ b/GLOSSARY.md @@ -0,0 +1,68 @@ +TODO dvdsk copy latest from notes + +These are some terms and structures frequently used throughout the zed codebase. This is a best effort list. PR's are very welcome. + +Questions: + +- Can we generate this list form doc comments throughout zed? + +## GPUI + +- `Context`: A wrapper around the App struct with specialized behavior for a specific Entity +- `App`: A singleton which holds the full application state including all the entities. +- `AsyncApp`: +- `Window`: +- `Task`: +- `Executor`: + - both background and foreground +- `Entity`: A strong, well-typed reference to a struct which is managed by gpui. Effectively a pointer into the Context. +- `Global`: A singleton type which has only one value +- `Event`: A datatype which can be send by an Entity to subscribers +- `Action`: An event that represents a user's keyboard input that can be handled by listerners + Example: `file finder: toggle` +- `Subscription`: An event handler that is used to react to the changes of state in the application. + 1. Emitted event handling + 2. Observing {new,release,on notify} of an entity +- `Focus`: The place where keystrokes are handled first +- `Element`: An type that can be rendered +- `AnyElement`: A type erased version of an Element +- `element expression`: An expression that builds an element tree, example: +``` +h_flex() + .id(text[i]) + .relative() + .when(selected, |this| { + this.child( + div() + .h_4() + .absolute() + etc etc +``` + +## Zed + +- `Workspace`: The root of the window +- `Buffer`: The in memory representation of a 'file' together with relevant data such as syntax trees, git status and diagnostics. +- `Picker`: A model showing a list of items. When an item is selected you can confirm the item and then something happens. +- `PickerDelegate`: A trait used to specialize behavior for a Picker +- `Modal`: todo! +- `Project`: todo! +- `Stores`: todo! + +## Collab + +- `Upstream client`: The zed client which has shared their workspace +- `Downstream client`: The zed client joining a shared workspace + +## Debugger + +- `DapStore`: Is an entity that manages debugger sessions +- `debugger::Session`: Is an entity that manages the lifecycle of a debug session and communication with DAPS +- `BreakpointStore`: Is an entity that manages breakpoints states in local and remote instances of Zed +- `DebugSession`: Manages a debug session's UI and running state +- `RunningState`: Directily manages all the views of a debug session +- `VariableList`: The variable and watch list view of a debug session +- `Console` +- `Terminal` +- `BreakpointList` +- ` From 3a5d02cb7d6c1a7d79c101979df107b86e9ced07 Mon Sep 17 00:00:00 2001 From: David Kleingeld Date: Mon, 25 Aug 2025 16:13:09 +0200 Subject: [PATCH 2/7] in progress add buffer processor --- Cargo.lock | 99 ++++++++++++++++++- Cargo.toml | 2 +- crates/livekit_client/Cargo.toml | 3 +- crates/livekit_client/src/lib.rs | 66 ++++++------- .../src/livekit_client/playback.rs | 46 +++++++++ .../src/livekit_client/playback/source.rs | 81 ++++++++++++++- crates/livekit_client/src/record.rs | 7 +- 7 files changed, 265 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b3d7b2691..30be5d582c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9628,6 +9628,7 @@ dependencies = [ "core-video", "coreaudio-rs 0.12.1", "cpal", + "dasp_sample", "futures 0.3.31", "gpui", "gpui_tokio", @@ -13873,14 +13874,15 @@ dependencies = [ [[package]] name = "rodio" version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e40ecf59e742e03336be6a3d53755e789fd05a059fa22dfa0ed624722319e183" +source = "git+https://github.com/RustAudio/rodio?branch=microphone#9af0d470b35c6e1ec6bca88d87bbf4bc52f1f018" dependencies = [ "cpal", "dasp_sample", "hound", "num-rational", + "rtrb", "symphonia", + "thiserror 2.0.12", "tracing", ] @@ -13955,6 +13957,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rtrb" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8388ea1a9e0ea807e442e8263a699e7edcb320ecbcd21b4fa8ff859acce3ba" + [[package]] name = "rules_library" version = "0.1.0" @@ -15954,12 +15962,53 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "815c942ae7ee74737bb00f965fa5b5a2ac2ce7b6c01c0cc169bbeaf7abd5f5a9" dependencies = [ "lazy_static", + "symphonia-bundle-flac", + "symphonia-bundle-mp3", + "symphonia-codec-aac", "symphonia-codec-pcm", + "symphonia-codec-vorbis", "symphonia-core", + "symphonia-format-isomp4", + "symphonia-format-ogg", "symphonia-format-riff", "symphonia-metadata", ] +[[package]] +name = "symphonia-bundle-flac" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e34f34298a7308d4397a6c7fbf5b84c5d491231ce3dd379707ba673ab3bd97" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-bundle-mp3" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c01c2aae70f0f1fb096b6f0ff112a930b1fb3626178fba3ae68b09dce71706d4" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-codec-aac" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbf25b545ad0d3ee3e891ea643ad115aff4ca92f6aec472086b957a58522f70" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", +] + [[package]] name = "symphonia-codec-pcm" version = "0.5.4" @@ -15970,6 +16019,17 @@ dependencies = [ "symphonia-core", ] +[[package]] +name = "symphonia-codec-vorbis" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a98765fb46a0a6732b007f7e2870c2129b6f78d87db7987e6533c8f164a9f30" +dependencies = [ + "log", + "symphonia-core", + "symphonia-utils-xiph", +] + [[package]] name = "symphonia-core" version = "0.5.4" @@ -15983,6 +16043,31 @@ dependencies = [ "log", ] +[[package]] +name = "symphonia-format-isomp4" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfdf178d697e50ce1e5d9b982ba1b94c47218e03ec35022d9f0e071a16dc844" +dependencies = [ + "encoding_rs", + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-format-ogg" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ada3505789516bcf00fc1157c67729eded428b455c27ca370e41f4d785bfa931" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + [[package]] name = "symphonia-format-riff" version = "0.5.4" @@ -16007,6 +16092,16 @@ dependencies = [ "symphonia-core", ] +[[package]] +name = "symphonia-utils-xiph" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "484472580fa49991afda5f6550ece662237b00c6f562c7d9638d1b086ed010fe" +dependencies = [ + "symphonia-core", + "symphonia-metadata", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/Cargo.toml b/Cargo.toml index 84de9b30ad..debd2cdaf1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -361,7 +361,7 @@ remote_server = { path = "crates/remote_server" } repl = { path = "crates/repl" } reqwest_client = { path = "crates/reqwest_client" } rich_text = { path = "crates/rich_text" } -rodio = { version = "0.21.1", default-features = false } +rodio = { git = "https://github.com/RustAudio/rodio", branch = "microphone"} rope = { path = "crates/rope" } rpc = { path = "crates/rpc" } rules_library = { path = "crates/rules_library" } diff --git a/crates/livekit_client/Cargo.toml b/crates/livekit_client/Cargo.toml index 3575325ac0..467c2f7437 100644 --- a/crates/livekit_client/Cargo.toml +++ b/crates/livekit_client/Cargo.toml @@ -41,7 +41,8 @@ tokio-tungstenite.workspace = true util.workspace = true workspace-hack.workspace = true -rodio = { workspace = true, features = ["wav_output"] } +rodio = { workspace = true, features = ["wav_output", "recording"] } +dasp_sample = "0.11" [target.'cfg(not(any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")))'.dependencies] libwebrtc = { rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d", git = "https://github.com/zed-industries/livekit-rust-sdks" } diff --git a/crates/livekit_client/src/lib.rs b/crates/livekit_client/src/lib.rs index 055aa3704e..71638df2ec 100644 --- a/crates/livekit_client/src/lib.rs +++ b/crates/livekit_client/src/lib.rs @@ -9,19 +9,19 @@ use rodio::DeviceTrait as _; mod record; pub use record::CaptureInput; -#[cfg(not(any( - test, - feature = "test-support", - all(target_os = "windows", target_env = "gnu"), - target_os = "freebsd" -)))] +// #[cfg(not(any( +// test, +// feature = "test-support", +// all(target_os = "windows", target_env = "gnu"), +// target_os = "freebsd" +// )))] mod livekit_client; -#[cfg(not(any( - test, - feature = "test-support", - all(target_os = "windows", target_env = "gnu"), - target_os = "freebsd" -)))] +// #[cfg(not(any( +// test, +// feature = "test-support", +// all(target_os = "windows", target_env = "gnu"), +// target_os = "freebsd" +// )))] pub use livekit_client::*; // If you need proper LSP in livekit_client you've got to comment @@ -29,27 +29,27 @@ pub use livekit_client::*; // - the mods: mock_client & test and their conditional blocks // - the pub use mock_client::* and their conditional blocks -#[cfg(any( - test, - feature = "test-support", - all(target_os = "windows", target_env = "gnu"), - target_os = "freebsd" -))] -mod mock_client; -#[cfg(any( - test, - feature = "test-support", - all(target_os = "windows", target_env = "gnu"), - target_os = "freebsd" -))] -pub mod test; -#[cfg(any( - test, - feature = "test-support", - all(target_os = "windows", target_env = "gnu"), - target_os = "freebsd" -))] -pub use mock_client::*; +// #[cfg(any( +// test, +// feature = "test-support", +// all(target_os = "windows", target_env = "gnu"), +// target_os = "freebsd" +// ))] +// mod mock_client; +// #[cfg(any( +// test, +// feature = "test-support", +// all(target_os = "windows", target_env = "gnu"), +// target_os = "freebsd" +// ))] +// pub mod test; +// #[cfg(any( +// test, +// feature = "test-support", +// all(target_os = "windows", target_env = "gnu"), +// target_os = "freebsd" +// ))] +// pub use mock_client::*; #[derive(Debug, Clone)] pub enum Participant { diff --git a/crates/livekit_client/src/livekit_client/playback.rs b/crates/livekit_client/src/livekit_client/playback.rs index d6b64dbaca..63ce404bc2 100644 --- a/crates/livekit_client/src/livekit_client/playback.rs +++ b/crates/livekit_client/src/livekit_client/playback.rs @@ -1,6 +1,8 @@ use anyhow::{Context as _, Result}; +use cpal::Sample; use cpal::traits::{DeviceTrait, StreamTrait as _}; +use dasp_sample::ToSample; use futures::channel::mpsc::UnboundedSender; use futures::{Stream, StreamExt as _}; use gpui::{ @@ -19,7 +21,9 @@ use livekit::webrtc::{ }; use parking_lot::Mutex; use rodio::Source; +use rodio::source::{LimitSettings, UniformSourceIterator}; use std::cell::RefCell; +use std::num::NonZero; use std::sync::Weak; use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; use std::time::Duration; @@ -254,6 +258,48 @@ impl AudioStack { } } + async fn capture_input_rodio( + apm: Arc>, + frame_tx: UnboundedSender>, + sample_rate: u32, + num_channels: u32, + ) -> Result<()> { + use crate::livekit_client::playback::source::RodioExt; + thread::spawn(|| { + let stream = rodio::microphone::MicrophoneBuilder::new() + .with_default_device()? + .with_default_config()? + .open_stream()?; + let stream = UniformSourceIterator::new( + stream, + NonZero::new(1).expect("1 is not zero"), + NonZero::new(SAMPLE_RATE).expect("constant is not zero"), + ) + .limit(LimitSettings::live_performance()) + .process_buffer(|buffer| { + let mut int_buffer: [i16; _] = buffer.map(|s| s.to_sample()); + apm.lock() + .process_stream(&mut int_buffer, sample_rate as i32, num_channels as i32) + .unwrap(); // TODO dvdsk fix this + for (sample, processed) in buffer.iter().zip(&int_buffer) { + *sample = (*processed).to_sample_(); + } + }) + .automatic_gain_control(1.0, 4.0, 0.0, 5.0); + + loop { + frame_tx.unbounded_send(AudioFrame { + data: Cow::Owned(sampled), + sample_rate, + num_channels, + samples_per_channel: sample_rate / 100, + }) + } + }); + + Ok(()) + } + async fn capture_input( apm: Arc>, frame_tx: UnboundedSender>, diff --git a/crates/livekit_client/src/livekit_client/playback/source.rs b/crates/livekit_client/src/livekit_client/playback/source.rs index 021640247d..0ce3e6aa1c 100644 --- a/crates/livekit_client/src/livekit_client/playback/source.rs +++ b/crates/livekit_client/src/livekit_client/playback/source.rs @@ -1,3 +1,5 @@ +use std::num::NonZero; + use futures::StreamExt; use libwebrtc::{audio_stream::native::NativeAudioStream, prelude::AudioFrame}; use livekit::track::RemoteAudioTrack; @@ -9,7 +11,11 @@ fn frame_to_samplesbuffer(frame: AudioFrame) -> SamplesBuffer { let samples = frame.data.iter().copied(); let samples = SampleTypeConverter::<_, _>::new(samples); let samples: Vec = samples.collect(); - SamplesBuffer::new(frame.num_channels as u16, frame.sample_rate, samples) + SamplesBuffer::new( + NonZero::new(frame.num_channels as u16).expect("audio frame channels is nonzero"), + NonZero::new(frame.sample_rate).expect("audio frame sample rate is nonzero"), + samples, + ) } pub struct LiveKitStream { @@ -65,3 +71,76 @@ impl Source for LiveKitStream { self.inner.total_duration() } } + +pub trait RodioExt: Source + Sized { + fn process_buffer( + self, + callback: impl FnMut(&mut [rodio::Sample; 200]), + ) -> ProcessBuffer + where + F: FnMut(&mut [rodio::Sample]); +} + +impl RodioExt for S { + fn process_buffer( + self, + callback: impl FnMut(&mut [rodio::Sample; 200]), + ) -> ProcessBuffer + where + F: FnMut(&mut [rodio::Sample]), + { + ProcessBuffer { + inner: self, + callback, + in_buffer: [0.0; 200], + out_buffer: [0.0; 200], + } + } +} + +pub struct ProcessBuffer +where + S: Source + Sized, + F: FnMut(&mut [rodio::Sample; 200]), +{ + inner: S, + callback: F, + in_buffer: [rodio::Sample; 200], + out_buffer: std::array::IntoIter, +} + +impl Iterator for ProcessBuffer +where + S: Source + Sized, + F: FnMut(&mut [rodio::Sample; 200]), +{ + type Item = rodio::Sample; + + fn next(&mut self) -> Option { + for sample in &mut in_buffer { + *sample = self.inner.next()?; + } + } +} + +impl Source for ProcessBuffer +where + S: Source + Sized, + F: FnMut(&mut [rodio::Sample; 200]), +{ + fn current_span_len(&self) -> Option { + todo!() + } + + fn channels(&self) -> rodio::ChannelCount { + todo!() + } + + fn sample_rate(&self) -> rodio::SampleRate { + todo!() + } + + fn total_duration(&self) -> Option { + todo!() + } +} diff --git a/crates/livekit_client/src/record.rs b/crates/livekit_client/src/record.rs index 925c0d4c67..ad9d9435d9 100644 --- a/crates/livekit_client/src/record.rs +++ b/crates/livekit_client/src/record.rs @@ -1,5 +1,6 @@ use std::{ env, + num::NonZero, path::{Path, PathBuf}, sync::{Arc, Mutex}, time::Duration, @@ -83,7 +84,11 @@ fn write_out( .expect("Stream has ended, callback cant hold the lock"), ); let samples: Vec = SampleTypeConverter::<_, f32>::new(samples.into_iter()).collect(); - let mut samples = SamplesBuffer::new(config.channels(), config.sample_rate().0, samples); + let mut samples = SamplesBuffer::new( + NonZero::new(config.channels()).expect("config channel is never zero"), + NonZero::new(config.sample_rate().0).expect("config sample_rate is never zero"), + samples, + ); match rodio::output_to_wav(&mut samples, path) { Ok(_) => Ok(()), Err(e) => Err(anyhow::anyhow!("Failed to write wav file: {}", e)), From 9521f540fd567ad433f83a2906f12039749b6a36 Mon Sep 17 00:00:00 2001 From: David Kleingeld Date: Tue, 26 Aug 2025 12:12:01 +0200 Subject: [PATCH 3/7] adds ProcessBuffer as rodio Source --- Cargo.lock | 2 +- .../src/livekit_client/playback.rs | 30 ++++++++----- .../src/livekit_client/playback/source.rs | 45 ++++++++++--------- 3 files changed, 45 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30be5d582c..f0481db850 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13874,7 +13874,7 @@ dependencies = [ [[package]] name = "rodio" version = "0.21.1" -source = "git+https://github.com/RustAudio/rodio?branch=microphone#9af0d470b35c6e1ec6bca88d87bbf4bc52f1f018" +source = "git+https://github.com/RustAudio/rodio?branch=microphone#bb560f30b17d330459b81afc918f2a4a123c41aa" dependencies = [ "cpal", "dasp_sample", diff --git a/crates/livekit_client/src/livekit_client/playback.rs b/crates/livekit_client/src/livekit_client/playback.rs index 63ce404bc2..c7882e1f20 100644 --- a/crates/livekit_client/src/livekit_client/playback.rs +++ b/crates/livekit_client/src/livekit_client/playback.rs @@ -265,12 +265,12 @@ impl AudioStack { num_channels: u32, ) -> Result<()> { use crate::livekit_client::playback::source::RodioExt; - thread::spawn(|| { + thread::spawn(move || { let stream = rodio::microphone::MicrophoneBuilder::new() - .with_default_device()? - .with_default_config()? + .default_device()? + .default_config()? .open_stream()?; - let stream = UniformSourceIterator::new( + let mut stream = UniformSourceIterator::new( stream, NonZero::new(1).expect("1 is not zero"), NonZero::new(SAMPLE_RATE).expect("constant is not zero"), @@ -281,20 +281,28 @@ impl AudioStack { apm.lock() .process_stream(&mut int_buffer, sample_rate as i32, num_channels as i32) .unwrap(); // TODO dvdsk fix this - for (sample, processed) in buffer.iter().zip(&int_buffer) { + for (sample, processed) in buffer.iter_mut().zip(&int_buffer) { *sample = (*processed).to_sample_(); } }) .automatic_gain_control(1.0, 4.0, 0.0, 5.0); loop { - frame_tx.unbounded_send(AudioFrame { - data: Cow::Owned(sampled), - sample_rate, - num_channels, - samples_per_channel: sample_rate / 100, - }) + let sampled = stream.by_ref().take(1000).map(|s| s.to_sample()).collect(); + + if frame_tx + .unbounded_send(AudioFrame { + data: Cow::Owned(sampled), + sample_rate, + num_channels, + samples_per_channel: sample_rate / 100, + }) + .is_err() + { + break; + } } + Ok::<(), anyhow::Error>(()) }); Ok(()) diff --git a/crates/livekit_client/src/livekit_client/playback/source.rs b/crates/livekit_client/src/livekit_client/playback/source.rs index 0ce3e6aa1c..b54bc7e821 100644 --- a/crates/livekit_client/src/livekit_client/playback/source.rs +++ b/crates/livekit_client/src/livekit_client/playback/source.rs @@ -73,27 +73,21 @@ impl Source for LiveKitStream { } pub trait RodioExt: Source + Sized { - fn process_buffer( - self, - callback: impl FnMut(&mut [rodio::Sample; 200]), - ) -> ProcessBuffer + fn process_buffer(self, callback: F) -> ProcessBuffer where - F: FnMut(&mut [rodio::Sample]); + F: FnMut(&mut [rodio::Sample; 200]); } impl RodioExt for S { - fn process_buffer( - self, - callback: impl FnMut(&mut [rodio::Sample; 200]), - ) -> ProcessBuffer + fn process_buffer(self, callback: F) -> ProcessBuffer where - F: FnMut(&mut [rodio::Sample]), + F: FnMut(&mut [rodio::Sample; 200]), { ProcessBuffer { inner: self, callback, - in_buffer: [0.0; 200], - out_buffer: [0.0; 200], + buffer: [0.0; 200], + next: 200, } } } @@ -105,8 +99,8 @@ where { inner: S, callback: F, - in_buffer: [rodio::Sample; 200], - out_buffer: std::array::IntoIter, + buffer: [rodio::Sample; 200], + next: usize, } impl Iterator for ProcessBuffer @@ -117,30 +111,41 @@ where type Item = rodio::Sample; fn next(&mut self) -> Option { - for sample in &mut in_buffer { - *sample = self.inner.next()?; + self.next += 1; + if self.next < self.buffer.len() { + let sample = self.buffer[self.next]; + return Some(sample); } + + for sample in &mut self.buffer { + *sample = self.inner.next()? + } + (self.callback)(&mut self.buffer); + + self.next = 0; + Some(self.buffer[0]) } } +// TODO dvdsk this should be a spanless Source impl Source for ProcessBuffer where S: Source + Sized, F: FnMut(&mut [rodio::Sample; 200]), { fn current_span_len(&self) -> Option { - todo!() + None } fn channels(&self) -> rodio::ChannelCount { - todo!() + self.inner.channels() } fn sample_rate(&self) -> rodio::SampleRate { - todo!() + self.inner.sample_rate() } fn total_duration(&self) -> Option { - todo!() + self.inner.total_duration() } } From efd9edb5ff1c7b73c40f2838c7672cf6b62864a4 Mon Sep 17 00:00:00 2001 From: David Kleingeld Date: Tue, 26 Aug 2025 12:17:17 +0200 Subject: [PATCH 4/7] make process_buffer take bufsize const generic --- .../src/livekit_client/playback.rs | 11 ++++++-- .../src/livekit_client/playback/source.rs | 26 +++++++++---------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/crates/livekit_client/src/livekit_client/playback.rs b/crates/livekit_client/src/livekit_client/playback.rs index c7882e1f20..c6b317e37f 100644 --- a/crates/livekit_client/src/livekit_client/playback.rs +++ b/crates/livekit_client/src/livekit_client/playback.rs @@ -265,6 +265,9 @@ impl AudioStack { num_channels: u32, ) -> Result<()> { use crate::livekit_client::playback::source::RodioExt; + const NUM_CHANNELS: usize = 1; + const LIVEKIT_BUFFER_SIZE: usize = (SAMPLE_RATE as usize / 100) * NUM_CHANNELS as usize; + thread::spawn(move || { let stream = rodio::microphone::MicrophoneBuilder::new() .default_device()? @@ -276,7 +279,7 @@ impl AudioStack { NonZero::new(SAMPLE_RATE).expect("constant is not zero"), ) .limit(LimitSettings::live_performance()) - .process_buffer(|buffer| { + .process_buffer::(|buffer| { let mut int_buffer: [i16; _] = buffer.map(|s| s.to_sample()); apm.lock() .process_stream(&mut int_buffer, sample_rate as i32, num_channels as i32) @@ -288,7 +291,11 @@ impl AudioStack { .automatic_gain_control(1.0, 4.0, 0.0, 5.0); loop { - let sampled = stream.by_ref().take(1000).map(|s| s.to_sample()).collect(); + let sampled = stream + .by_ref() + .take(LIVEKIT_BUFFER_SIZE) + .map(|s| s.to_sample()) + .collect(); if frame_tx .unbounded_send(AudioFrame { diff --git a/crates/livekit_client/src/livekit_client/playback/source.rs b/crates/livekit_client/src/livekit_client/playback/source.rs index b54bc7e821..89e1fd0b21 100644 --- a/crates/livekit_client/src/livekit_client/playback/source.rs +++ b/crates/livekit_client/src/livekit_client/playback/source.rs @@ -73,40 +73,40 @@ impl Source for LiveKitStream { } pub trait RodioExt: Source + Sized { - fn process_buffer(self, callback: F) -> ProcessBuffer + fn process_buffer(self, callback: F) -> ProcessBuffer where - F: FnMut(&mut [rodio::Sample; 200]); + F: FnMut(&mut [rodio::Sample; N]); } impl RodioExt for S { - fn process_buffer(self, callback: F) -> ProcessBuffer + fn process_buffer(self, callback: F) -> ProcessBuffer where - F: FnMut(&mut [rodio::Sample; 200]), + F: FnMut(&mut [rodio::Sample; N]), { ProcessBuffer { inner: self, callback, - buffer: [0.0; 200], - next: 200, + buffer: [0.0; N], + next: N, } } } -pub struct ProcessBuffer +pub struct ProcessBuffer where S: Source + Sized, - F: FnMut(&mut [rodio::Sample; 200]), + F: FnMut(&mut [rodio::Sample; N]), { inner: S, callback: F, - buffer: [rodio::Sample; 200], + buffer: [rodio::Sample; N], next: usize, } -impl Iterator for ProcessBuffer +impl Iterator for ProcessBuffer where S: Source + Sized, - F: FnMut(&mut [rodio::Sample; 200]), + F: FnMut(&mut [rodio::Sample; N]), { type Item = rodio::Sample; @@ -128,10 +128,10 @@ where } // TODO dvdsk this should be a spanless Source -impl Source for ProcessBuffer +impl Source for ProcessBuffer where S: Source + Sized, - F: FnMut(&mut [rodio::Sample; 200]), + F: FnMut(&mut [rodio::Sample; N]), { fn current_span_len(&self) -> Option { None From 5a4f284e456a3c995ab50dc9f30e0dd67380c19a Mon Sep 17 00:00:00 2001 From: David Kleingeld Date: Tue, 26 Aug 2025 12:37:10 +0200 Subject: [PATCH 5/7] adds error handling to audio input pipeline --- .../src/livekit_client/playback.rs | 36 ++++++++++++------- .../src/livekit_client/playback/source.rs | 4 +-- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/crates/livekit_client/src/livekit_client/playback.rs b/crates/livekit_client/src/livekit_client/playback.rs index c6b317e37f..b908c27ed2 100644 --- a/crates/livekit_client/src/livekit_client/playback.rs +++ b/crates/livekit_client/src/livekit_client/playback.rs @@ -26,9 +26,10 @@ use std::cell::RefCell; use std::num::NonZero; use std::sync::Weak; use std::sync::atomic::{AtomicBool, AtomicI32, Ordering}; +use std::sync::mpsc::{TryRecvError, channel}; use std::time::Duration; use std::{borrow::Cow, collections::VecDeque, sync::Arc, thread}; -use util::{ResultExt as _, maybe}; +use util::{ResultExt as _, debug_panic, maybe}; mod source; @@ -268,6 +269,8 @@ impl AudioStack { const NUM_CHANNELS: usize = 1; const LIVEKIT_BUFFER_SIZE: usize = (SAMPLE_RATE as usize / 100) * NUM_CHANNELS as usize; + let (stream_error_tx, stream_error_rx) = channel(); + thread::spawn(move || { let stream = rodio::microphone::MicrophoneBuilder::new() .default_device()? @@ -281,11 +284,16 @@ impl AudioStack { .limit(LimitSettings::live_performance()) .process_buffer::(|buffer| { let mut int_buffer: [i16; _] = buffer.map(|s| s.to_sample()); - apm.lock() - .process_stream(&mut int_buffer, sample_rate as i32, num_channels as i32) - .unwrap(); // TODO dvdsk fix this - for (sample, processed) in buffer.iter_mut().zip(&int_buffer) { - *sample = (*processed).to_sample_(); + if let Err(e) = apm + .lock() + .process_stream(&mut int_buffer, SAMPLE_RATE as i32, NUM_CHANNELS as i32) + .context("livekit audio processor error") + { + let _ = stream_error_tx.send(e); + } else { + for (sample, processed) in buffer.iter_mut().zip(&int_buffer) { + *sample = (*processed).to_sample_(); + } } }) .automatic_gain_control(1.0, 4.0, 0.0, 5.0); @@ -297,19 +305,23 @@ impl AudioStack { .map(|s| s.to_sample()) .collect(); - if frame_tx + match stream_error_rx.try_recv() { + Ok(apm_error) => return Err::<(), _>(apm_error), + Err(TryRecvError::Disconnected) => { + debug_panic!("Stream should end on its own without sending an error") + } + Err(TryRecvError::Empty) => (), + } + + frame_tx .unbounded_send(AudioFrame { data: Cow::Owned(sampled), sample_rate, num_channels, samples_per_channel: sample_rate / 100, }) - .is_err() - { - break; - } + .context("Failed to send audio frame")? } - Ok::<(), anyhow::Error>(()) }); Ok(()) diff --git a/crates/livekit_client/src/livekit_client/playback/source.rs b/crates/livekit_client/src/livekit_client/playback/source.rs index 89e1fd0b21..7f9f5e1b2a 100644 --- a/crates/livekit_client/src/livekit_client/playback/source.rs +++ b/crates/livekit_client/src/livekit_client/playback/source.rs @@ -103,7 +103,7 @@ where next: usize, } -impl Iterator for ProcessBuffer +impl Iterator for ProcessBuffer where S: Source + Sized, F: FnMut(&mut [rodio::Sample; N]), @@ -127,13 +127,13 @@ where } } -// TODO dvdsk this should be a spanless Source impl Source for ProcessBuffer where S: Source + Sized, F: FnMut(&mut [rodio::Sample; N]), { fn current_span_len(&self) -> Option { + // TODO dvdsk this should be a spanless Source None } From cef721465f94a3646341f6992eb869eb7888e76b Mon Sep 17 00:00:00 2001 From: David Kleingeld Date: Tue, 26 Aug 2025 15:06:52 +0200 Subject: [PATCH 6/7] move apm (webrtc audio processor) to audio crate --- Cargo.lock | 40 ++++---- crates/audio/Cargo.toml | 4 + crates/audio/src/audio.rs | 38 +++++++- crates/audio/src/rodio_ext.rs | 93 +++++++++++++++++++ crates/livekit_client/src/livekit_client.rs | 2 +- .../src/livekit_client/playback.rs | 64 +++++++++---- .../src/livekit_client/playback/source.rs | 85 +---------------- 7 files changed, 205 insertions(+), 121 deletions(-) create mode 100644 crates/audio/src/rodio_ext.rs diff --git a/Cargo.lock b/Cargo.lock index f0481db850..6f901fe829 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1381,6 +1381,8 @@ dependencies = [ "anyhow", "collections", "gpui", + "libwebrtc", + "parking_lot", "rodio", "schemars", "serde", @@ -2681,7 +2683,7 @@ dependencies = [ "cap-primitives", "cap-std", "io-lifetimes", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2710,7 +2712,7 @@ dependencies = [ "maybe-owned", "rustix 1.0.7", "rustix-linux-procfs", - "windows-sys 0.59.0", + "windows-sys 0.52.0", "winx", ] @@ -5327,7 +5329,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -5714,7 +5716,7 @@ checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", "rustix 1.0.7", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6092,7 +6094,7 @@ checksum = "94e7099f6313ecacbe1256e8ff9d617b75d1bcb16a6fddef94866d225a01a14a" dependencies = [ "io-lifetimes", "rustix 1.0.7", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8551,7 +8553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2285ddfe3054097ef4b2fe909ef8c3bcd1ea52a8f0d274416caebeef39f04a65" dependencies = [ "io-lifetimes", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8624,7 +8626,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi 0.5.0", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -8706,7 +8708,7 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -9406,7 +9408,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -13062,7 +13064,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -13874,7 +13876,7 @@ dependencies = [ [[package]] name = "rodio" version = "0.21.1" -source = "git+https://github.com/RustAudio/rodio?branch=microphone#bb560f30b17d330459b81afc918f2a4a123c41aa" +source = "git+https://github.com/RustAudio/rodio?branch=microphone#cad73716a363a5ba92fcb73ec37a4b98a7d7de5f" dependencies = [ "cpal", "dasp_sample", @@ -14104,7 +14106,7 @@ dependencies = [ "itoa", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -14117,7 +14119,7 @@ dependencies = [ "errno 0.3.11", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -14239,7 +14241,7 @@ dependencies = [ "security-framework 3.2.0", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -15591,7 +15593,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -16249,7 +16251,7 @@ dependencies = [ "fd-lock", "io-lifetimes", "rustix 0.38.44", - "windows-sys 0.59.0", + "windows-sys 0.52.0", "winx", ] @@ -16431,7 +16433,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.7", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -18993,7 +18995,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -19652,7 +19654,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" dependencies = [ "bitflags 2.9.0", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] diff --git a/crates/audio/Cargo.toml b/crates/audio/Cargo.toml index ae7eb52fd3..8826b5f49c 100644 --- a/crates/audio/Cargo.toml +++ b/crates/audio/Cargo.toml @@ -19,6 +19,10 @@ gpui.workspace = true settings.workspace = true schemars.workspace = true serde.workspace = true +parking_lot.workspace = true rodio = { workspace = true, features = [ "wav", "playback", "tracing" ] } util.workspace = true workspace-hack.workspace = true + +[target.'cfg(not(any(all(target_os = "windows", target_env = "gnu"), target_os = "freebsd")))'.dependencies] +libwebrtc = { rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d", git = "https://github.com/zed-industries/livekit-rust-sdks" } diff --git a/crates/audio/src/audio.rs b/crates/audio/src/audio.rs index b4f2c24fef..a5a4e721d3 100644 --- a/crates/audio/src/audio.rs +++ b/crates/audio/src/audio.rs @@ -1,12 +1,15 @@ use anyhow::{Context as _, Result, anyhow}; use collections::HashMap; use gpui::{App, BorrowAppContext, Global}; -use rodio::{Decoder, OutputStream, OutputStreamBuilder, Source, source::Buffered}; +use libwebrtc::native::apm; +use parking_lot::Mutex; +use rodio::{Decoder, OutputStream, OutputStreamBuilder, Source, mixer::Mixer, source::Buffered}; use settings::Settings; -use std::io::Cursor; +use std::{io::Cursor, sync::Arc}; use util::ResultExt; mod audio_settings; +mod rodio_ext; pub use audio_settings::AudioSettings; pub fn init(cx: &mut App) { @@ -38,18 +41,46 @@ impl Sound { } } -#[derive(Default)] pub struct Audio { output_handle: Option, + output_mixer: Option, + echo_canceller: Arc>, source_cache: HashMap>>>>, } +impl Default for Audio { + fn default() -> Self { + Self { + output_handle: Default::default(), + output_mixer: Default::default(), + echo_canceller: Arc::new(Mutex::new(apm::AudioProcessingModule::new( + true, false, false, false, + ))), + source_cache: Default::default(), + } + } +} + impl Global for Audio {} impl Audio { fn ensure_output_exists(&mut self) -> Option<&OutputStream> { if self.output_handle.is_none() { self.output_handle = OutputStreamBuilder::open_default_stream().log_err(); + if let Some(output_handle) = self.output_handle { + let config = output_handle.config(); + let (mixer, source) = + rodio::mixer::mixer(config.channel_count(), config.sample_rate()); + self.output_mixer = Some(mixer); + + let echo_canceller = Arc::clone(&self.echo_canceller); + let source = source.inspect_buffered( + |buffer| echo_canceller.lock().process_reverse_stream(&mut buf), + config.sample_rate().get() as i32, + config.channel_count().get().into(), + ); + output_handle.mixer().add(source); + } } self.output_handle.as_ref() @@ -72,6 +103,7 @@ impl Audio { cx.update_default_global(|this: &mut Self, cx| { let source = this.sound_source(sound, cx).log_err()?; let output_handle = this.ensure_output_exists()?; + output_handle.mixer().add(source); Some(()) }); diff --git a/crates/audio/src/rodio_ext.rs b/crates/audio/src/rodio_ext.rs new file mode 100644 index 0000000000..055ff9003a --- /dev/null +++ b/crates/audio/src/rodio_ext.rs @@ -0,0 +1,93 @@ +use rodio::Source; + +pub trait RodioExt: Source + Sized { + fn process_buffer(self, callback: F) -> ProcessBuffer + where + F: FnMut(&mut [rodio::Sample; N]); + fn inspect_buffer(self, callback: F) -> ProcessBuffer + where + F: FnMut(&[rodio::Sample; N]); +} + +impl RodioExt for S { + fn process_buffer(self, callback: F) -> ProcessBuffer + where + F: FnMut(&mut [rodio::Sample; N]), + { + ProcessBuffer { + inner: self, + callback, + buffer: [0.0; N], + next: N, + } + } + fn inspect_buffer(self, callback: F) -> ProcessBuffer + where + F: FnMut(&[rodio::Sample; N]), + { + InspectBuffer { + inner: self, + callback, + buffer: [0.0; N], + next: N, + } + } +} + +pub struct ProcessBuffer +where + S: Source + Sized, + F: FnMut(&mut [rodio::Sample; N]), +{ + inner: S, + callback: F, + buffer: [rodio::Sample; N], + next: usize, +} + +impl Iterator for ProcessBuffer +where + S: Source + Sized, + F: FnMut(&mut [rodio::Sample; N]), +{ + type Item = rodio::Sample; + + fn next(&mut self) -> Option { + self.next += 1; + if self.next < self.buffer.len() { + let sample = self.buffer[self.next]; + return Some(sample); + } + + for sample in &mut self.buffer { + *sample = self.inner.next()? + } + (self.callback)(&mut self.buffer); + + self.next = 0; + Some(self.buffer[0]) + } +} + +impl Source for ProcessBuffer +where + S: Source + Sized, + F: FnMut(&mut [rodio::Sample; N]), +{ + fn current_span_len(&self) -> Option { + // TODO dvdsk this should be a spanless Source + None + } + + fn channels(&self) -> rodio::ChannelCount { + self.inner.channels() + } + + fn sample_rate(&self) -> rodio::SampleRate { + self.inner.sample_rate() + } + + fn total_duration(&self) -> Option { + self.inner.total_duration() + } +} diff --git a/crates/livekit_client/src/livekit_client.rs b/crates/livekit_client/src/livekit_client.rs index 0751b014f4..c14a29bee3 100644 --- a/crates/livekit_client/src/livekit_client.rs +++ b/crates/livekit_client/src/livekit_client.rs @@ -99,7 +99,7 @@ impl Room { &self, cx: &mut AsyncApp, ) -> Result<(LocalTrackPublication, playback::AudioStream)> { - let (track, stream) = self.playback.capture_local_microphone_track()?; + let (track, stream) = self.playback.capture_local_microphone_track(&cx)?; let publication = self .local_participant() .publish_track( diff --git a/crates/livekit_client/src/livekit_client/playback.rs b/crates/livekit_client/src/livekit_client/playback.rs index b908c27ed2..8fa0e7929a 100644 --- a/crates/livekit_client/src/livekit_client/playback.rs +++ b/crates/livekit_client/src/livekit_client/playback.rs @@ -1,12 +1,14 @@ use anyhow::{Context as _, Result}; +use audio::AudioSettings; use cpal::Sample; use cpal::traits::{DeviceTrait, StreamTrait as _}; use dasp_sample::ToSample; use futures::channel::mpsc::UnboundedSender; use futures::{Stream, StreamExt as _}; use gpui::{ - BackgroundExecutor, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream, Task, + AsyncApp, BackgroundExecutor, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream, + Task, }; use libwebrtc::native::{apm, audio_mixer, audio_resampler}; use livekit::track; @@ -19,9 +21,11 @@ use livekit::webrtc::{ video_source::{RtcVideoSource, VideoResolution, native::NativeVideoSource}, video_stream::native::NativeVideoStream, }; +use log::info; use parking_lot::Mutex; use rodio::Source; use rodio::source::{LimitSettings, UniformSourceIterator}; +use settings::Settings; use std::cell::RefCell; use std::num::NonZero; use std::sync::Weak; @@ -45,8 +49,8 @@ pub(crate) struct AudioStack { // 16kHz, 32kHz and 48kHz. As 48 is the most common "next step up" // for audio output devices like speakers/bluetooth, we just hard-code // this; and downsample when we need to. -const SAMPLE_RATE: u32 = 48000; -const NUM_CHANNELS: u32 = 2; +const SAMPLE_RATE: NonZero = NonZero::new(48000).expect("not zero"); +const NUM_CHANNELS: NonZero = NonZero::new(2).expect("not zero"); pub(crate) fn play_remote_audio_track( track: &livekit::track::RemoteAudioTrack, @@ -55,6 +59,7 @@ pub(crate) fn play_remote_audio_track( let stop_handle = Arc::new(AtomicBool::new(false)); let stop_handle_clone = stop_handle.clone(); let stream = source::LiveKitStream::new(cx.background_executor(), track) + .process_buffer(|| apm) .stoppable() .periodic_access(Duration::from_millis(50), move |s| { if stop_handle.load(Ordering::Relaxed) { @@ -95,8 +100,8 @@ impl AudioStack { let next_ssrc = self.next_ssrc.fetch_add(1, Ordering::Relaxed); let source = AudioMixerSource { ssrc: next_ssrc, - sample_rate: SAMPLE_RATE, - num_channels: NUM_CHANNELS, + sample_rate: SAMPLE_RATE.get(), + num_channels: NUM_CHANNELS.get() as u32, buffer: Arc::default(), }; self.mixer.lock().add_source(source.clone()); @@ -136,7 +141,7 @@ impl AudioStack { let apm = self.apm.clone(); let mixer = self.mixer.clone(); async move { - Self::play_output(apm, mixer, SAMPLE_RATE, NUM_CHANNELS) + Self::play_output(apm, mixer, SAMPLE_RATE.get(), NUM_CHANNELS.get().into()) .await .log_err(); } @@ -147,12 +152,13 @@ impl AudioStack { pub(crate) fn capture_local_microphone_track( &self, + cx: &AsyncApp, ) -> Result<(crate::LocalAudioTrack, AudioStream)> { let source = NativeAudioSource::new( // n.b. this struct's options are always ignored, noise cancellation is provided by apm. AudioSourceOptions::default(), - SAMPLE_RATE, - NUM_CHANNELS, + SAMPLE_RATE.get(), + NUM_CHANNELS.get().into(), 10, ); @@ -171,8 +177,16 @@ impl AudioStack { } } }); + let rodio_pipeline = + AudioSettings::try_read_global(cx, |setting| setting.rodio_audio).unwrap_or(false); let capture_task = self.executor.spawn(async move { - Self::capture_input(apm, frame_tx, SAMPLE_RATE, NUM_CHANNELS).await + if rodio_pipeline { + info!("Using experimental.rodio_audio audio pipeline"); + Self::capture_input_rodio(apm, frame_tx).await + } else { + Self::capture_input(apm, frame_tx, SAMPLE_RATE.get(), NUM_CHANNELS.get().into()) + .await + } }); let on_drop = util::defer(|| { @@ -262,12 +276,11 @@ impl AudioStack { async fn capture_input_rodio( apm: Arc>, frame_tx: UnboundedSender>, - sample_rate: u32, - num_channels: u32, ) -> Result<()> { use crate::livekit_client::playback::source::RodioExt; const NUM_CHANNELS: usize = 1; - const LIVEKIT_BUFFER_SIZE: usize = (SAMPLE_RATE as usize / 100) * NUM_CHANNELS as usize; + const LIVEKIT_BUFFER_SIZE: usize = + (SAMPLE_RATE.get() as usize / 100) * NUM_CHANNELS as usize; let (stream_error_tx, stream_error_rx) = channel(); @@ -275,18 +288,31 @@ impl AudioStack { let stream = rodio::microphone::MicrophoneBuilder::new() .default_device()? .default_config()? + .prefer_sample_rates([ + SAMPLE_RATE, + SAMPLE_RATE.saturating_mul(NonZero::new(2).expect("not zero")), + ]) + .prefer_channel_counts([ + NonZero::new(1).expect("not zero"), + NonZero::new(2).expect("not zero"), + ]) + .prefer_buffer_sizes(512..) .open_stream()?; let mut stream = UniformSourceIterator::new( stream, NonZero::new(1).expect("1 is not zero"), - NonZero::new(SAMPLE_RATE).expect("constant is not zero"), + SAMPLE_RATE, ) .limit(LimitSettings::live_performance()) .process_buffer::(|buffer| { let mut int_buffer: [i16; _] = buffer.map(|s| s.to_sample()); if let Err(e) = apm .lock() - .process_stream(&mut int_buffer, SAMPLE_RATE as i32, NUM_CHANNELS as i32) + .process_stream( + &mut int_buffer, + SAMPLE_RATE.get() as i32, + NUM_CHANNELS as i32, + ) .context("livekit audio processor error") { let _ = stream_error_tx.send(e); @@ -299,7 +325,7 @@ impl AudioStack { .automatic_gain_control(1.0, 4.0, 0.0, 5.0); loop { - let sampled = stream + let sampled: Vec<_> = stream .by_ref() .take(LIVEKIT_BUFFER_SIZE) .map(|s| s.to_sample()) @@ -315,10 +341,10 @@ impl AudioStack { frame_tx .unbounded_send(AudioFrame { + sample_rate: SAMPLE_RATE.get(), + num_channels: NUM_CHANNELS as u32, + samples_per_channel: sampled.len() as u32 / NUM_CHANNELS as u32, data: Cow::Owned(sampled), - sample_rate, - num_channels, - samples_per_channel: sample_rate / 100, }) .context("Failed to send audio frame")? } @@ -419,6 +445,8 @@ impl AudioStack { } } +use crate::livekit_client::playback::source::RodioExt; + use super::LocalVideoTrack; pub enum AudioStream { diff --git a/crates/livekit_client/src/livekit_client/playback/source.rs b/crates/livekit_client/src/livekit_client/playback/source.rs index 7f9f5e1b2a..62d949f95d 100644 --- a/crates/livekit_client/src/livekit_client/playback/source.rs +++ b/crates/livekit_client/src/livekit_client/playback/source.rs @@ -26,8 +26,11 @@ pub struct LiveKitStream { impl LiveKitStream { pub fn new(executor: &gpui::BackgroundExecutor, track: &RemoteAudioTrack) -> Self { - let mut stream = - NativeAudioStream::new(track.rtc_track(), SAMPLE_RATE as i32, NUM_CHANNELS as i32); + let mut stream = NativeAudioStream::new( + track.rtc_track(), + SAMPLE_RATE.get() as i32, + NUM_CHANNELS.get().into(), + ); let (queue_input, queue_output) = rodio::queue::queue(true); // spawn rtc stream let receiver_task = executor.spawn({ @@ -71,81 +74,3 @@ impl Source for LiveKitStream { self.inner.total_duration() } } - -pub trait RodioExt: Source + Sized { - fn process_buffer(self, callback: F) -> ProcessBuffer - where - F: FnMut(&mut [rodio::Sample; N]); -} - -impl RodioExt for S { - fn process_buffer(self, callback: F) -> ProcessBuffer - where - F: FnMut(&mut [rodio::Sample; N]), - { - ProcessBuffer { - inner: self, - callback, - buffer: [0.0; N], - next: N, - } - } -} - -pub struct ProcessBuffer -where - S: Source + Sized, - F: FnMut(&mut [rodio::Sample; N]), -{ - inner: S, - callback: F, - buffer: [rodio::Sample; N], - next: usize, -} - -impl Iterator for ProcessBuffer -where - S: Source + Sized, - F: FnMut(&mut [rodio::Sample; N]), -{ - type Item = rodio::Sample; - - fn next(&mut self) -> Option { - self.next += 1; - if self.next < self.buffer.len() { - let sample = self.buffer[self.next]; - return Some(sample); - } - - for sample in &mut self.buffer { - *sample = self.inner.next()? - } - (self.callback)(&mut self.buffer); - - self.next = 0; - Some(self.buffer[0]) - } -} - -impl Source for ProcessBuffer -where - S: Source + Sized, - F: FnMut(&mut [rodio::Sample; N]), -{ - fn current_span_len(&self) -> Option { - // TODO dvdsk this should be a spanless Source - None - } - - fn channels(&self) -> rodio::ChannelCount { - self.inner.channels() - } - - fn sample_rate(&self) -> rodio::SampleRate { - self.inner.sample_rate() - } - - fn total_duration(&self) -> Option { - self.inner.total_duration() - } -} From 0a434c5cd45ecf98207abc5c33f003d63184ce49 Mon Sep 17 00:00:00 2001 From: David Kleingeld Date: Tue, 26 Aug 2025 15:10:14 +0200 Subject: [PATCH 7/7] fix workspace-hack --- .config/hakari.toml | 2 +- Cargo.lock | 2 ++ tooling/workspace-hack/Cargo.toml | 26 ++++++++++++++++++++------ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/.config/hakari.toml b/.config/hakari.toml index 8ce0b77490..cbfb3cc1dc 100644 --- a/.config/hakari.toml +++ b/.config/hakari.toml @@ -26,7 +26,7 @@ third-party = [ # build of remote_server should not include scap / its x11 dependency { name = "scap", git = "https://github.com/zed-industries/scap", rev = "808aa5c45b41e8f44729d02e38fd00a2fe2722e7" }, # build of remote_server should not need to include on libalsa through rodio - { name = "rodio" }, + # { name = "rodio" }, ] [final-excludes] diff --git a/Cargo.lock b/Cargo.lock index 6f901fe829..a06f51b937 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20031,6 +20031,7 @@ dependencies = [ "libsqlite3-sys", "linux-raw-sys 0.4.15", "linux-raw-sys 0.9.4", + "livekit-runtime", "log", "lyon", "lyon_path", @@ -20072,6 +20073,7 @@ dependencies = [ "regex-automata 0.4.9", "regex-syntax 0.8.5", "ring", + "rodio", "rust_decimal", "rustc-hash 1.1.0", "rustix 0.38.44", diff --git a/tooling/workspace-hack/Cargo.toml b/tooling/workspace-hack/Cargo.toml index 054e757056..d0047962bd 100644 --- a/tooling/workspace-hack/Cargo.toml +++ b/tooling/workspace-hack/Cargo.toml @@ -103,6 +103,7 @@ regalloc2 = { version = "0.11", features = ["checker", "enable-serde"] } regex = { version = "1" } regex-automata = { version = "0.4" } regex-syntax = { version = "0.8" } +rodio = { git = "https://github.com/RustAudio/rodio", branch = "microphone", features = ["tracing", "wav_output"] } rust_decimal = { version = "1", default-features = false, features = ["maths", "serde", "std"] } rustc-hash = { version = "1" } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", default-features = false, features = ["fs", "net", "std"] } @@ -237,6 +238,7 @@ regalloc2 = { version = "0.11", features = ["checker", "enable-serde"] } regex = { version = "1" } regex-automata = { version = "0.4" } regex-syntax = { version = "0.8" } +rodio = { git = "https://github.com/RustAudio/rodio", branch = "microphone", features = ["tracing", "wav_output"] } rust_decimal = { version = "1", default-features = false, features = ["maths", "serde", "std"] } rustc-hash = { version = "1" } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", default-features = false, features = ["fs", "net", "std"] } @@ -291,6 +293,7 @@ getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-f gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] } hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] } itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } +livekit-runtime = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d" } naga = { version = "25", features = ["msl-out", "wgsl-in"] } nix-b73a96c0a5f6a7d9 = { package = "nix", version = "0.29", features = ["fs", "pthread", "signal", "user"] } objc2 = { version = "0.6" } @@ -319,6 +322,7 @@ getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-f gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] } hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] } itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } +livekit-runtime = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d" } naga = { version = "25", features = ["msl-out", "wgsl-in"] } nix-b73a96c0a5f6a7d9 = { package = "nix", version = "0.29", features = ["fs", "pthread", "signal", "user"] } objc2 = { version = "0.6" } @@ -348,6 +352,7 @@ getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-f gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] } hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] } itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } +livekit-runtime = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d" } naga = { version = "25", features = ["msl-out", "wgsl-in"] } nix-b73a96c0a5f6a7d9 = { package = "nix", version = "0.29", features = ["fs", "pthread", "signal", "user"] } objc2 = { version = "0.6" } @@ -376,6 +381,7 @@ getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-f gimli = { version = "0.31", default-features = false, features = ["read", "std", "write"] } hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] } itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } +livekit-runtime = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d" } naga = { version = "25", features = ["msl-out", "wgsl-in"] } nix-b73a96c0a5f6a7d9 = { package = "nix", version = "0.29", features = ["fs", "pthread", "signal", "user"] } objc2 = { version = "0.6" } @@ -414,6 +420,7 @@ inout = { version = "0.1", default-features = false, features = ["block-padding" itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } linux-raw-sys-274715c4dabd11b0 = { package = "linux-raw-sys", version = "0.9", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "xdp"] } linux-raw-sys-9fbad63c4bcf4a8f = { package = "linux-raw-sys", version = "0.4", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "system", "xdp"] } +livekit-runtime = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d" } mio = { version = "1", features = ["net", "os-ext"] } naga = { version = "25", features = ["spv-out", "wgsl-in"] } nix-1f5adca70f036a62 = { package = "nix", version = "0.28", features = ["fs", "mman", "ptrace", "signal", "term", "user"] } @@ -455,6 +462,7 @@ inout = { version = "0.1", default-features = false, features = ["block-padding" itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } linux-raw-sys-274715c4dabd11b0 = { package = "linux-raw-sys", version = "0.9", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "xdp"] } linux-raw-sys-9fbad63c4bcf4a8f = { package = "linux-raw-sys", version = "0.4", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "system", "xdp"] } +livekit-runtime = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d" } mio = { version = "1", features = ["net", "os-ext"] } naga = { version = "25", features = ["spv-out", "wgsl-in"] } nix-1f5adca70f036a62 = { package = "nix", version = "0.28", features = ["fs", "mman", "ptrace", "signal", "term", "user"] } @@ -494,6 +502,7 @@ inout = { version = "0.1", default-features = false, features = ["block-padding" itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } linux-raw-sys-274715c4dabd11b0 = { package = "linux-raw-sys", version = "0.9", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "xdp"] } linux-raw-sys-9fbad63c4bcf4a8f = { package = "linux-raw-sys", version = "0.4", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "system", "xdp"] } +livekit-runtime = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d" } mio = { version = "1", features = ["net", "os-ext"] } naga = { version = "25", features = ["spv-out", "wgsl-in"] } nix-1f5adca70f036a62 = { package = "nix", version = "0.28", features = ["fs", "mman", "ptrace", "signal", "term", "user"] } @@ -535,6 +544,7 @@ inout = { version = "0.1", default-features = false, features = ["block-padding" itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } linux-raw-sys-274715c4dabd11b0 = { package = "linux-raw-sys", version = "0.9", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "xdp"] } linux-raw-sys-9fbad63c4bcf4a8f = { package = "linux-raw-sys", version = "0.4", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "system", "xdp"] } +livekit-runtime = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d" } mio = { version = "1", features = ["net", "os-ext"] } naga = { version = "25", features = ["spv-out", "wgsl-in"] } nix-1f5adca70f036a62 = { package = "nix", version = "0.28", features = ["fs", "mman", "ptrace", "signal", "term", "user"] } @@ -564,6 +574,7 @@ getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-f getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] } hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] } itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } +livekit-runtime = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d" } ring = { version = "0.17", features = ["std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["event"] } scopeguard = { version = "1" } @@ -575,9 +586,9 @@ tower = { version = "0.5", default-features = false, features = ["timeout", "uti winapi = { version = "0.3", default-features = false, features = ["cfg", "commapi", "consoleapi", "errhandlingapi", "evntrace", "fileapi", "handleapi", "impl-debug", "impl-default", "in6addr", "inaddr", "ioapiset", "knownfolders", "minwinbase", "minwindef", "namedpipeapi", "ntsecapi", "objbase", "processenv", "processthreadsapi", "shlobj", "std", "synchapi", "sysinfoapi", "timezoneapi", "winbase", "windef", "winerror", "winioctl", "winnt", "winreg", "winsock2", "winuser"] } windows-core = { version = "0.61" } windows-numerics = { version = "0.2" } -windows-sys-73dcd821b1037cfd = { package = "windows-sys", version = "0.59", features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Win32_Globalization", "Win32_NetworkManagement_IpHelper", "Win32_Networking_WinSock", "Win32_Security_Authentication_Identity", "Win32_Security_Credentials", "Win32_Security_Cryptography", "Win32_Storage_FileSystem", "Win32_System_Com", "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Ioctl", "Win32_System_Kernel", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Performance", "Win32_System_Pipes", "Win32_System_Registry", "Win32_System_SystemInformation", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming", "Win32_UI_Input_KeyboardAndMouse", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] } -windows-sys-b21d60becc0929df = { package = "windows-sys", version = "0.52", features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Wdk_System_IO", "Win32_Foundation", "Win32_Networking_WinSock", "Win32_Security_Authorization", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_IO", "Win32_System_Memory", "Win32_System_Pipes", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_WindowsProgramming"] } -windows-sys-c8eced492e86ede7 = { package = "windows-sys", version = "0.48", features = ["Win32_Foundation", "Win32_Globalization", "Win32_Networking_WinSock", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Com", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Pipes", "Win32_System_Registry", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming", "Win32_UI_Shell"] } +windows-sys-73dcd821b1037cfd = { package = "windows-sys", version = "0.59", features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Win32_Globalization", "Win32_Networking_WinSock", "Win32_Security_Authentication_Identity", "Win32_Security_Credentials", "Win32_Security_Cryptography", "Win32_Storage_FileSystem", "Win32_System_Com", "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Kernel", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Registry", "Win32_System_SystemInformation", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming", "Win32_UI_Input_KeyboardAndMouse", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] } +windows-sys-b21d60becc0929df = { package = "windows-sys", version = "0.52", features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Wdk_System_IO", "Win32_Foundation", "Win32_NetworkManagement_IpHelper", "Win32_Networking_WinSock", "Win32_Security_Authorization", "Win32_Security_Cryptography", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Ioctl", "Win32_System_Kernel", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Performance", "Win32_System_Pipes", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming"] } +windows-sys-c8eced492e86ede7 = { package = "windows-sys", version = "0.48", features = ["Win32_Foundation", "Win32_Globalization", "Win32_Networking_WinSock", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Com", "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Pipes", "Win32_System_Registry", "Win32_System_SystemInformation", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming", "Win32_UI_Shell"] } [target.x86_64-pc-windows-msvc.build-dependencies] codespan-reporting = { version = "0.12" } @@ -587,6 +598,7 @@ getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3", default-f getrandom-6f8ce4dd05d13bba = { package = "getrandom", version = "0.2", default-features = false, features = ["js", "rdrand"] } hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "http2", "native-tokio", "ring", "tls12"] } itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } +livekit-runtime = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d" } proc-macro2 = { version = "1", default-features = false, features = ["span-locations"] } ring = { version = "0.17", features = ["std"] } rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38", features = ["event"] } @@ -599,9 +611,9 @@ tower = { version = "0.5", default-features = false, features = ["timeout", "uti winapi = { version = "0.3", default-features = false, features = ["cfg", "commapi", "consoleapi", "errhandlingapi", "evntrace", "fileapi", "handleapi", "impl-debug", "impl-default", "in6addr", "inaddr", "ioapiset", "knownfolders", "minwinbase", "minwindef", "namedpipeapi", "ntsecapi", "objbase", "processenv", "processthreadsapi", "shlobj", "std", "synchapi", "sysinfoapi", "timezoneapi", "winbase", "windef", "winerror", "winioctl", "winnt", "winreg", "winsock2", "winuser"] } windows-core = { version = "0.61" } windows-numerics = { version = "0.2" } -windows-sys-73dcd821b1037cfd = { package = "windows-sys", version = "0.59", features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Win32_Globalization", "Win32_NetworkManagement_IpHelper", "Win32_Networking_WinSock", "Win32_Security_Authentication_Identity", "Win32_Security_Credentials", "Win32_Security_Cryptography", "Win32_Storage_FileSystem", "Win32_System_Com", "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Ioctl", "Win32_System_Kernel", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Performance", "Win32_System_Pipes", "Win32_System_Registry", "Win32_System_SystemInformation", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming", "Win32_UI_Input_KeyboardAndMouse", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] } -windows-sys-b21d60becc0929df = { package = "windows-sys", version = "0.52", features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Wdk_System_IO", "Win32_Foundation", "Win32_Networking_WinSock", "Win32_Security_Authorization", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_IO", "Win32_System_Memory", "Win32_System_Pipes", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_WindowsProgramming"] } -windows-sys-c8eced492e86ede7 = { package = "windows-sys", version = "0.48", features = ["Win32_Foundation", "Win32_Globalization", "Win32_Networking_WinSock", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Com", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Pipes", "Win32_System_Registry", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming", "Win32_UI_Shell"] } +windows-sys-73dcd821b1037cfd = { package = "windows-sys", version = "0.59", features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Win32_Globalization", "Win32_Networking_WinSock", "Win32_Security_Authentication_Identity", "Win32_Security_Credentials", "Win32_Security_Cryptography", "Win32_Storage_FileSystem", "Win32_System_Com", "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Kernel", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Registry", "Win32_System_SystemInformation", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming", "Win32_UI_Input_KeyboardAndMouse", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] } +windows-sys-b21d60becc0929df = { package = "windows-sys", version = "0.52", features = ["Wdk_Foundation", "Wdk_Storage_FileSystem", "Wdk_System_IO", "Win32_Foundation", "Win32_NetworkManagement_IpHelper", "Win32_Networking_WinSock", "Win32_Security_Authorization", "Win32_Security_Cryptography", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Ioctl", "Win32_System_Kernel", "Win32_System_LibraryLoader", "Win32_System_Memory", "Win32_System_Performance", "Win32_System_Pipes", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming"] } +windows-sys-c8eced492e86ede7 = { package = "windows-sys", version = "0.48", features = ["Win32_Foundation", "Win32_Globalization", "Win32_Networking_WinSock", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Com", "Win32_System_Console", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Pipes", "Win32_System_Registry", "Win32_System_SystemInformation", "Win32_System_Threading", "Win32_System_Time", "Win32_System_WindowsProgramming", "Win32_UI_Shell"] } [target.x86_64-unknown-linux-musl.dependencies] aes = { version = "0.8", default-features = false, features = ["zeroize"] } @@ -621,6 +633,7 @@ inout = { version = "0.1", default-features = false, features = ["block-padding" itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } linux-raw-sys-274715c4dabd11b0 = { package = "linux-raw-sys", version = "0.9", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "xdp"] } linux-raw-sys-9fbad63c4bcf4a8f = { package = "linux-raw-sys", version = "0.4", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "system", "xdp"] } +livekit-runtime = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d" } mio = { version = "1", features = ["net", "os-ext"] } naga = { version = "25", features = ["spv-out", "wgsl-in"] } nix-1f5adca70f036a62 = { package = "nix", version = "0.28", features = ["fs", "mman", "ptrace", "signal", "term", "user"] } @@ -662,6 +675,7 @@ inout = { version = "0.1", default-features = false, features = ["block-padding" itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12" } linux-raw-sys-274715c4dabd11b0 = { package = "linux-raw-sys", version = "0.9", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "xdp"] } linux-raw-sys-9fbad63c4bcf4a8f = { package = "linux-raw-sys", version = "0.4", default-features = false, features = ["elf", "errno", "general", "if_ether", "ioctl", "net", "netlink", "no_std", "prctl", "system", "xdp"] } +livekit-runtime = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "5f04705ac3f356350ae31534ffbc476abc9ea83d" } mio = { version = "1", features = ["net", "os-ext"] } naga = { version = "25", features = ["spv-out", "wgsl-in"] } nix-1f5adca70f036a62 = { package = "nix", version = "0.28", features = ["fs", "mman", "ptrace", "signal", "term", "user"] }