Detailed changes
@@ -1278,7 +1278,6 @@ name = "audio"
version = "0.1.0"
dependencies = [
"anyhow",
- "async-tar",
"collections",
"cpal",
"crossbeam",
@@ -1290,7 +1289,6 @@ dependencies = [
"rodio",
"serde",
"settings",
- "smol",
"thiserror 2.0.17",
"util",
]
@@ -464,10 +464,6 @@
"button_layout": "platform_default",
},
"audio": {
- // Opt into the new audio system.
- "experimental.rodio_audio": false,
- // Requires 'rodio_audio: true'
- //
// Automatically increase or decrease you microphone's volume. This affects how
// loud you sound to others.
//
@@ -476,33 +472,10 @@
// audio and has auto speaker volume on this will make you very loud
// compared to other speakers.
"experimental.auto_microphone_volume": false,
- // Requires 'rodio_audio: true'
- //
- // Automatically increate or decrease the volume of other call members.
- // This only affects how things sound for you.
- "experimental.auto_speaker_volume": true,
- // Requires 'rodio_audio: true'
- //
- // Remove background noises. Works great for typing, cars, dogs, AC. Does
- // not work well on music.
- "experimental.denoise": true,
- // Requires 'rodio_audio: true'
- //
- // Use audio parameters compatible with the previous versions of
- // experimental audio and non-experimental audio. When this is false you
- // will sound strange to anyone not on the latest experimental audio. In
- // the future we will migrate by setting this to false
- //
- // You need to rejoin a call for this setting to apply
- "experimental.legacy_audio_compatible": true,
- // Requires 'rodio_audio: true'
- //
// Select specific output audio device.
// `null` means use system default.
// Any unrecognized output device will fall back to system default.
"experimental.output_audio_device": null,
- // Requires 'rodio_audio: true'
- //
// Select specific input audio device.
// `null` means use system default.
// Any unrecognized input device will fall back to system default.
@@ -14,7 +14,6 @@ doctest = false
[dependencies]
anyhow.workspace = true
-async-tar.workspace = true
collections.workspace = true
cpal.workspace = true
crossbeam.workspace = true
@@ -25,7 +24,6 @@ parking_lot.workspace = true
rodio.workspace = true
serde.workspace = true
settings.workspace = true
-smol.workspace = true
thiserror.workspace = true
util.workspace = true
@@ -11,7 +11,7 @@ pub use audio_settings::AudioSettings;
pub use audio_settings::LIVE_SETTINGS;
mod audio_pipeline;
-pub use audio_pipeline::{Audio, VoipParts};
+pub use audio_pipeline::Audio;
pub use audio_pipeline::{AudioDeviceInfo, AvailableAudioDevices};
pub use audio_pipeline::{ensure_devices_initialized, resolve_device};
// TODO(audio) replace with input test functionality in the audio crate
@@ -4,23 +4,17 @@ use cpal::{
DeviceDescription, DeviceId, default_host,
traits::{DeviceTrait, HostTrait},
};
-use gpui::{App, AsyncApp, BackgroundExecutor, BorrowAppContext, Global};
+use gpui::{App, AsyncApp, BorrowAppContext, Global};
pub(super) use cpal::Sample;
-pub(super) use rodio::source::LimitSettings;
-use rodio::{
- Decoder, DeviceSinkBuilder, MixerDeviceSink, Source,
- mixer::Mixer,
- source::{AutomaticGainControlSettings, Buffered},
-};
+use rodio::{Decoder, DeviceSinkBuilder, MixerDeviceSink, Source, mixer::Mixer, source::Buffered};
use settings::Settings;
-use std::{io::Cursor, path::PathBuf, sync::atomic::Ordering, time::Duration};
+use std::io::Cursor;
use util::ResultExt;
mod echo_canceller;
use echo_canceller::EchoCanceller;
-mod replays;
mod rodio_ext;
pub use crate::audio_settings::AudioSettings;
pub use rodio_ext::RodioExt;
@@ -59,7 +53,6 @@ pub struct Audio {
output: Option<(MixerDeviceSink, Mixer)>,
pub echo_canceller: EchoCanceller,
source_cache: HashMap<Sound, Buffered<Decoder<Cursor<Vec<u8>>>>>,
- replays: replays::Replays,
}
impl Global for Audio {}
@@ -84,84 +77,6 @@ impl Audio {
.expect("we only get here if opening the outputstream succeeded"))
}
- pub fn save_replays(
- &self,
- executor: BackgroundExecutor,
- ) -> gpui::Task<anyhow::Result<(PathBuf, Duration)>> {
- self.replays.replays_to_tar(executor)
- }
-
- pub fn open_microphone(mut voip_parts: VoipParts) -> anyhow::Result<impl Source> {
- let stream = open_input_stream(voip_parts.input_audio_device)?;
- let stream = stream
- .possibly_disconnected_channels_to_mono()
- .constant_params(CHANNEL_COUNT, SAMPLE_RATE)
- .process_buffer::<BUFFER_SIZE, _>(move |buffer| {
- let mut int_buffer: [i16; _] = buffer.map(|s| s.to_sample());
- if voip_parts
- .echo_canceller
- .process_stream(&mut int_buffer)
- .log_err()
- .is_some()
- {
- for (sample, processed) in buffer.iter_mut().zip(&int_buffer) {
- *sample = (*processed).to_sample();
- }
- }
- })
- .limit(LimitSettings::live_performance())
- .automatic_gain_control(AutomaticGainControlSettings {
- target_level: 0.90,
- attack_time: Duration::from_secs(1),
- release_time: Duration::from_secs(0),
- absolute_max_gain: 5.0,
- })
- .periodic_access(Duration::from_millis(100), move |agc_source| {
- agc_source
- .set_enabled(LIVE_SETTINGS.auto_microphone_volume.load(Ordering::Relaxed));
- let _ = LIVE_SETTINGS.denoise; // TODO(audio: re-introduce de-noising
- });
-
- let (replay, stream) = stream.replayable(crate::REPLAY_DURATION)?;
- voip_parts
- .replays
- .add_voip_stream("local microphone".to_string(), replay);
-
- Ok(stream)
- }
-
- pub fn play_voip_stream(
- source: impl rodio::Source + Send + 'static,
- speaker_name: String,
- is_staff: bool,
- cx: &mut App,
- ) -> anyhow::Result<()> {
- let (replay_source, source) = source
- .automatic_gain_control(AutomaticGainControlSettings {
- target_level: 0.90,
- attack_time: Duration::from_secs(1),
- release_time: Duration::from_secs(0),
- absolute_max_gain: 5.0,
- })
- .periodic_access(Duration::from_millis(100), move |agc_source| {
- agc_source.set_enabled(LIVE_SETTINGS.auto_speaker_volume.load(Ordering::Relaxed));
- })
- .replayable(crate::REPLAY_DURATION)
- .expect("REPLAY_DURATION is longer than 100ms");
- let output_audio_device = AudioSettings::get_global(cx).output_audio_device.clone();
-
- cx.update_default_global(|this: &mut Self, _cx| {
- let output_mixer = this
- .ensure_output_exists(output_audio_device)
- .context("Could not get output mixer")?;
- output_mixer.add(source);
- if is_staff {
- this.replays.add_voip_stream(speaker_name, replay_source);
- }
- Ok(())
- })
- }
-
pub fn play_sound(sound: Sound, cx: &mut App) {
let output_audio_device = AudioSettings::get_global(cx).output_audio_device.clone();
cx.update_default_global(|this: &mut Self, cx| {
@@ -203,29 +118,6 @@ impl Audio {
}
}
-pub struct VoipParts {
- echo_canceller: EchoCanceller,
- replays: replays::Replays,
- input_audio_device: Option<DeviceId>,
-}
-
-impl VoipParts {
- pub fn new(cx: &AsyncApp) -> anyhow::Result<Self> {
- let (apm, replays) = cx.read_default_global::<Audio, _>(|audio, _| {
- (audio.echo_canceller.clone(), audio.replays.clone())
- });
- let input_audio_device =
- AudioSettings::try_read_global(cx, |settings| settings.input_audio_device.clone())
- .flatten();
-
- Ok(Self {
- echo_canceller: apm,
- replays,
- input_audio_device,
- })
- }
-}
-
pub fn open_input_stream(
device_id: Option<DeviceId>,
) -> anyhow::Result<rodio::microphone::Microphone> {
@@ -1,78 +0,0 @@
-use anyhow::{Context, anyhow};
-use async_tar::{Builder, Header};
-use gpui::{BackgroundExecutor, Task};
-
-use collections::HashMap;
-use parking_lot::Mutex;
-use rodio::Source;
-use smol::fs::File;
-use std::{io, path::PathBuf, sync::Arc, time::Duration};
-
-use crate::REPLAY_DURATION;
-use crate::audio_pipeline::rodio_ext::Replay;
-
-#[derive(Default, Clone)]
-pub(crate) struct Replays(Arc<Mutex<HashMap<String, Replay>>>);
-
-impl Replays {
- pub(crate) fn add_voip_stream(&self, stream_name: String, source: Replay) {
- let mut map = self.0.lock();
- map.retain(|_, replay| replay.source_is_active());
- map.insert(stream_name, source);
- }
-
- pub(crate) fn replays_to_tar(
- &self,
- executor: BackgroundExecutor,
- ) -> Task<anyhow::Result<(PathBuf, Duration)>> {
- let map = Arc::clone(&self.0);
- executor.spawn(async move {
- let recordings: Vec<_> = map
- .lock()
- .iter_mut()
- .map(|(name, replay)| {
- let queued = REPLAY_DURATION.min(replay.duration_ready());
- (name.clone(), replay.take_duration(queued).record())
- })
- .collect();
- let longest = recordings
- .iter()
- .map(|(_, r)| {
- r.total_duration()
- .expect("SamplesBuffer always returns a total duration")
- })
- .max()
- .ok_or(anyhow!("There is no audio to capture"))?;
-
- let path = std::env::current_dir()
- .context("Could not get current dir")?
- .join("replays.tar");
- let tar = File::create(&path)
- .await
- .context("Could not create file for tar")?;
-
- let mut tar = Builder::new(tar);
-
- for (name, recording) in recordings {
- let mut writer = io::Cursor::new(Vec::new());
- rodio::wav_to_writer(recording, &mut writer).context("failed to encode wav")?;
- let wav_data = writer.into_inner();
- let path = name.replace(' ', "_") + ".wav";
- let mut header = Header::new_gnu();
- // rw permissions for everyone
- header.set_mode(0o666);
- header.set_size(wav_data.len() as u64);
- tar.append_data(&mut header, path, wav_data.as_slice())
- .await
- .context("failed to apped wav to tar")?;
- }
- tar.into_inner()
- .await
- .context("Could not finish writing tar")?
- .sync_all()
- .await
- .context("Could not flush tar file to disk")?;
- Ok((path, longest))
- })
- }
-}
@@ -9,12 +9,6 @@ use settings::{RegisterSetting, Settings, SettingsStore};
#[derive(Clone, Debug, RegisterSetting)]
pub struct AudioSettings {
- /// Opt into the new audio system.
- ///
- /// You need to rejoin a call for this setting to apply
- pub rodio_audio: bool, // default is false
- /// Requires 'rodio_audio: true'
- ///
/// Automatically increase or decrease you microphone's volume. This affects how
/// loud you sound to others.
///
@@ -23,25 +17,6 @@ pub struct AudioSettings {
/// audio and has auto speaker volume on this will make you very loud
/// compared to other speakers.
pub auto_microphone_volume: bool,
- /// Requires 'rodio_audio: true'
- ///
- /// Automatically increate or decrease the volume of other call members.
- /// This only affects how things sound for you.
- pub auto_speaker_volume: bool,
- /// Requires 'rodio_audio: true'
- ///
- /// Remove background noises. Works great for typing, cars, dogs, AC. Does
- /// not work well on music.
- pub denoise: bool,
- /// Requires 'rodio_audio: true'
- ///
- /// Use audio parameters compatible with the previous versions of
- /// experimental audio and non-experimental audio. When this is false you
- /// will sound strange to anyone not on the latest experimental audio. In
- /// the future we will migrate by setting this to false
- ///
- /// You need to rejoin a call for this setting to apply
- pub legacy_audio_compatible: bool,
/// Select specific output audio device.
pub output_audio_device: Option<DeviceId>,
/// Select specific input audio device.
@@ -53,11 +28,7 @@ impl Settings for AudioSettings {
fn from_settings(content: &settings::SettingsContent) -> Self {
let audio = &content.audio.as_ref().unwrap();
AudioSettings {
- rodio_audio: audio.rodio_audio.unwrap(),
auto_microphone_volume: audio.auto_microphone_volume.unwrap(),
- auto_speaker_volume: audio.auto_speaker_volume.unwrap(),
- denoise: audio.denoise.unwrap(),
- legacy_audio_compatible: audio.legacy_audio_compatible.unwrap(),
output_audio_device: audio
.output_audio_device
.as_ref()
@@ -73,8 +44,6 @@ impl Settings for AudioSettings {
/// See docs on [LIVE_SETTINGS]
pub struct LiveSettings {
pub auto_microphone_volume: AtomicBool,
- pub(crate) auto_speaker_volume: AtomicBool,
- pub(crate) denoise: AtomicBool,
}
impl LiveSettings {
@@ -84,24 +53,6 @@ impl LiveSettings {
AudioSettings::get_global(cx).auto_microphone_volume,
Ordering::Relaxed,
);
- LIVE_SETTINGS.auto_speaker_volume.store(
- AudioSettings::get_global(cx).auto_speaker_volume,
- Ordering::Relaxed,
- );
-
- let denoise_enabled = AudioSettings::get_global(cx).denoise;
- #[cfg(debug_assertions)]
- {
- static DENOISE_WARNING_SEND: AtomicBool = AtomicBool::new(false);
- if denoise_enabled && !DENOISE_WARNING_SEND.load(Ordering::Relaxed) {
- DENOISE_WARNING_SEND.store(true, Ordering::Relaxed);
- log::warn!("Denoise does not work on debug builds, not enabling")
- }
- }
- #[cfg(not(debug_assertions))]
- LIVE_SETTINGS
- .denoise
- .store(denoise_enabled, Ordering::Relaxed);
})
.detach();
@@ -109,18 +60,6 @@ impl LiveSettings {
LIVE_SETTINGS
.auto_microphone_volume
.store(init_settings.auto_microphone_volume, Ordering::Relaxed);
- LIVE_SETTINGS
- .auto_speaker_volume
- .store(init_settings.auto_speaker_volume, Ordering::Relaxed);
- let denoise_enabled = AudioSettings::get_global(cx).denoise;
- #[cfg(debug_assertions)]
- if denoise_enabled {
- log::warn!("Denoise does not work on debug builds, not enabling")
- }
- #[cfg(not(debug_assertions))]
- LIVE_SETTINGS
- .denoise
- .store(denoise_enabled, Ordering::Relaxed);
}
}
@@ -130,6 +69,4 @@ impl LiveSettings {
/// use the background executor.
pub static LIVE_SETTINGS: LiveSettings = LiveSettings {
auto_microphone_volume: AtomicBool::new(true),
- auto_speaker_volume: AtomicBool::new(true),
- denoise: AtomicBool::new(true),
};
@@ -1,10 +1,10 @@
-use anyhow::{Context as _, Result, anyhow};
+use anyhow::{Context as _, Result};
use audio::AudioSettings;
use collections::HashMap;
use futures::{SinkExt, channel::mpsc};
use gpui::{App, AsyncApp, ScreenCaptureSource, ScreenCaptureStream, Task};
use gpui_tokio::Tokio;
-use log::info;
+
use playback::capture_local_video_track;
use settings::Settings;
use std::sync::{Arc, atomic::AtomicU64};
@@ -13,10 +13,7 @@ use std::sync::{Arc, atomic::AtomicU64};
mod linux;
mod playback;
-use crate::{
- ConnectionQuality, LocalTrack, Participant, RemoteTrack, RoomEvent, TrackPublication,
- livekit_client::playback::Speaker,
-};
+use crate::{ConnectionQuality, LocalTrack, Participant, RemoteTrack, RoomEvent, TrackPublication};
pub use livekit::SessionStats;
pub use livekit::webrtc::stats::RtcStats;
pub use playback::AudioStream;
@@ -144,24 +141,10 @@ impl Room {
track: &RemoteAudioTrack,
cx: &mut App,
) -> Result<playback::AudioStream> {
- let speaker: Speaker =
- serde_urlencoded::from_str(&track.0.name()).unwrap_or_else(|_| Speaker {
- name: track.0.name(),
- is_staff: false,
- sends_legacy_audio: true,
- });
-
- if AudioSettings::get_global(cx).rodio_audio {
- info!("Using experimental.rodio_audio audio pipeline for output");
- playback::play_remote_audio_track(&track.0, speaker, cx)
- } else if speaker.sends_legacy_audio {
- let output_audio_device = AudioSettings::get_global(cx).output_audio_device.clone();
- Ok(self
- .playback
- .play_remote_audio_track(&track.0, output_audio_device))
- } else {
- Err(anyhow!("Client version too old to play audio in call"))
- }
+ let output_audio_device = AudioSettings::get_global(cx).output_audio_device.clone();
+ Ok(self
+ .playback
+ .play_remote_audio_track(&track.0, output_audio_device))
}
pub async fn get_stats(&self) -> Result<livekit::SessionStats> {
@@ -29,7 +29,7 @@ use serde::{Deserialize, Serialize};
use settings::Settings;
use std::cell::RefCell;
use std::sync::Weak;
-use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering};
+use std::sync::atomic::{AtomicI32, AtomicU64, Ordering};
use std::time::{Duration, Instant};
use std::{borrow::Cow, collections::VecDeque, sync::Arc};
use util::{ResultExt as _, maybe};
@@ -39,8 +39,6 @@ struct TimestampedFrame {
captured_at: Instant,
}
-mod source;
-
pub(crate) struct AudioStack {
executor: BackgroundExecutor,
apm: Arc<Mutex<apm::AudioProcessingModule>>,
@@ -49,38 +47,6 @@ pub(crate) struct AudioStack {
next_ssrc: AtomicI32,
}
-pub(crate) fn play_remote_audio_track(
- track: &livekit::track::RemoteAudioTrack,
- speaker: Speaker,
- cx: &mut gpui::App,
-) -> Result<AudioStream> {
- info!("speaker: {speaker:?}");
- let stream =
- source::LiveKitStream::new(cx.background_executor(), track, speaker.sends_legacy_audio);
-
- let stop_handle = Arc::new(AtomicBool::new(false));
- let stop_handle_clone = stop_handle.clone();
- let stream = stream
- .stoppable()
- .periodic_access(Duration::from_millis(50), move |s| {
- if stop_handle.load(Ordering::Relaxed) {
- s.stop();
- }
- });
-
- info!("sample_rate: {:?}", stream.sample_rate());
- info!("channel_count: {:?}", stream.channels());
- audio::Audio::play_voip_stream(stream, speaker.name, speaker.is_staff, cx)
- .context("Could not play audio")?;
-
- let on_drop = util::defer(move || {
- stop_handle_clone.store(true, Ordering::Relaxed);
- });
- Ok(AudioStream::Output {
- _drop: Box::new(on_drop),
- })
-}
-
impl AudioStack {
pub(crate) fn new(executor: BackgroundExecutor) -> Self {
let apm = Arc::new(Mutex::new(apm::AudioProcessingModule::new(
@@ -170,33 +136,17 @@ impl AudioStack {
is_staff: bool,
cx: &AsyncApp,
) -> Result<(crate::LocalAudioTrack, AudioStream, Arc<AtomicU64>)> {
- let legacy_audio_compatible =
- AudioSettings::try_read_global(cx, |setting| setting.legacy_audio_compatible)
- .unwrap_or(true);
-
- let source = if legacy_audio_compatible {
- NativeAudioSource::new(
- // n.b. this struct's options are always ignored, noise cancellation is provided by apm.
- AudioSourceOptions::default(),
- SAMPLE_RATE.get(), // TODO(audio): this was legacy params,
- // removed for now for simplicity
- CHANNEL_COUNT.get().into(),
- 10,
- )
- } else {
- NativeAudioSource::new(
- // n.b. this struct's options are always ignored, noise cancellation is provided by apm.
- AudioSourceOptions::default(),
- SAMPLE_RATE.get(),
- CHANNEL_COUNT.get().into(),
- 10,
- )
- };
+ let source = NativeAudioSource::new(
+ // n.b. this struct's options are always ignored, noise cancellation is provided by apm.
+ AudioSourceOptions::default(),
+ SAMPLE_RATE.get(),
+ CHANNEL_COUNT.get().into(),
+ 10,
+ );
let speaker = Speaker {
name: user_name,
is_staff,
- sends_legacy_audio: legacy_audio_compatible,
};
log::info!("Microphone speaker: {speaker:?}");
let track_name = serde_urlencoded::to_string(speaker)
@@ -221,21 +171,7 @@ impl AudioStack {
}
}
});
- let rodio_pipeline =
- AudioSettings::try_read_global(cx, |setting| setting.rodio_audio).unwrap_or_default();
- let capture_task = if rodio_pipeline {
- info!("Using experimental.rodio_audio audio pipeline");
- let voip_parts = audio::VoipParts::new(cx)?;
- // Audio needs to run real-time and should never be paused. That is
- // why we are using a normal std::thread and not a background task
- self.executor
- .spawn_with_priority(Priority::RealtimeAudio, async move {
- // microphone is non send on mac
- let microphone = audio::Audio::open_microphone(voip_parts)?;
- send_to_livekit(frame_tx, microphone);
- Ok(())
- })
- } else {
+ let capture_task = {
let input_audio_device =
AudioSettings::try_read_global(cx, |settings| settings.input_audio_device.clone())
.flatten();
@@ -546,40 +482,6 @@ impl rodio::Source for RodioEffectsAdaptor {
pub struct Speaker {
pub name: String,
pub is_staff: bool,
- pub sends_legacy_audio: bool,
-}
-
-fn send_to_livekit(mut frame_tx: Sender<TimestampedFrame>, mut microphone: impl Source) {
- use cpal::Sample;
- let sample_rate = microphone.sample_rate().get();
- let num_channels = microphone.channels().get() as u32;
- let buffer_size = sample_rate / 100 * num_channels;
-
- loop {
- let sampled: Vec<_> = microphone
- .by_ref()
- .take(buffer_size as usize)
- .map(|s| s.to_sample())
- .collect();
-
- match frame_tx.try_send(TimestampedFrame {
- frame: AudioFrame {
- sample_rate,
- num_channels,
- samples_per_channel: sampled.len() as u32 / num_channels,
- data: Cow::Owned(sampled),
- },
- captured_at: Instant::now(),
- }) {
- Ok(_) => {}
- Err(err) => {
- if !err.is_full() {
- // must rx has dropped or is not consuming
- break;
- }
- }
- }
- }
}
use super::LocalVideoTrack;
@@ -1,93 +0,0 @@
-use std::num::NonZero;
-
-use futures::StreamExt;
-use libwebrtc::{audio_stream::native::NativeAudioStream, prelude::AudioFrame};
-use livekit::track::RemoteAudioTrack;
-use rodio::{
- ChannelCount, SampleRate, Source, buffer::SamplesBuffer, conversions::SampleTypeConverter,
-};
-
-use audio::{CHANNEL_COUNT, SAMPLE_RATE};
-
-fn frame_to_samplesbuffer(frame: AudioFrame) -> SamplesBuffer {
- let samples = frame.data.iter().copied();
- let samples = SampleTypeConverter::<_, _>::new(samples);
- let samples: Vec<f32> = samples.collect();
- SamplesBuffer::new(
- NonZero::new(frame.num_channels as u16).expect("zero channels is nonsense"),
- NonZero::new(frame.sample_rate).expect("samplerate zero is nonsense"),
- samples,
- )
-}
-
-pub struct LiveKitStream {
- // shared_buffer: SharedBuffer,
- inner: rodio::queue::SourcesQueueOutput,
- _receiver_task: gpui::Task<()>,
- channel_count: ChannelCount,
- sample_rate: SampleRate,
-}
-
-impl LiveKitStream {
- pub fn new(
- executor: &gpui::BackgroundExecutor,
- track: &RemoteAudioTrack,
- legacy: bool,
- ) -> Self {
- let (channel_count, sample_rate) = if legacy {
- // (LEGACY_CHANNEL_COUNT, LEGACY_SAMPLE_RATE) TODO(audio): do this or remove
- (CHANNEL_COUNT, SAMPLE_RATE)
- } else {
- (CHANNEL_COUNT, SAMPLE_RATE)
- };
-
- let mut stream = NativeAudioStream::new(
- track.rtc_track(),
- sample_rate.get() as i32,
- channel_count.get().into(),
- );
- let (queue_input, queue_output) = rodio::queue::queue(true);
- // spawn rtc stream
- let receiver_task = executor.spawn_with_priority(gpui::Priority::RealtimeAudio, {
- async move {
- while let Some(frame) = stream.next().await {
- let samples = frame_to_samplesbuffer(frame);
- queue_input.append(samples);
- }
- }
- });
-
- LiveKitStream {
- _receiver_task: receiver_task,
- inner: queue_output,
- sample_rate,
- channel_count,
- }
- }
-}
-
-impl Iterator for LiveKitStream {
- type Item = rodio::Sample;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.inner.next()
- }
-}
-
-impl Source for LiveKitStream {
- fn current_span_len(&self) -> Option<usize> {
- self.inner.current_span_len()
- }
-
- fn channels(&self) -> rodio::ChannelCount {
- self.channel_count
- }
-
- fn sample_rate(&self) -> rodio::SampleRate {
- self.sample_rate
- }
-
- fn total_duration(&self) -> Option<std::time::Duration> {
- self.inner.total_duration()
- }
-}
@@ -322,13 +322,6 @@ impl strum::VariantNames for BaseKeymapContent {
#[with_fallible_options]
#[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)]
pub struct AudioSettingsContent {
- /// Opt into the new audio system.
- ///
- /// You need to rejoin a call for this setting to apply
- #[serde(rename = "experimental.rodio_audio")]
- pub rodio_audio: Option<bool>, // default is false
- /// Requires 'rodio_audio: true'
- ///
/// Automatically increase or decrease you microphone's volume. This affects how
/// loud you sound to others.
///
@@ -338,35 +331,11 @@ pub struct AudioSettingsContent {
/// compared to other speakers.
#[serde(rename = "experimental.auto_microphone_volume")]
pub auto_microphone_volume: Option<bool>,
- /// Requires 'rodio_audio: true'
- ///
- /// Automatically increate or decrease the volume of other call members.
- /// This only affects how things sound for you.
- #[serde(rename = "experimental.auto_speaker_volume")]
- pub auto_speaker_volume: Option<bool>,
- /// Requires 'rodio_audio: true'
- ///
/// Remove background noises. Works great for typing, cars, dogs, AC. Does
/// not work well on music.
- #[serde(rename = "experimental.denoise")]
- pub denoise: Option<bool>,
- /// Requires 'rodio_audio: true'
- ///
- /// Use audio parameters compatible with the previous versions of
- /// experimental audio and non-experimental audio. When this is false you
- /// will sound strange to anyone not on the latest experimental audio. In
- /// the future we will migrate by setting this to false
- ///
- /// You need to rejoin a call for this setting to apply
- #[serde(rename = "experimental.legacy_audio_compatible")]
- pub legacy_audio_compatible: Option<bool>,
- /// Requires 'rodio_audio: true'
- ///
/// Select specific output audio device.
#[serde(rename = "experimental.output_audio_device")]
pub output_audio_device: Option<AudioOutputDeviceName>,
- /// Requires 'rodio_audio: true'
- ///
/// Select specific input audio device.
#[serde(rename = "experimental.input_audio_device")]
pub input_audio_device: Option<AudioInputDeviceName>,
@@ -7038,101 +7038,8 @@ fn collaboration_page() -> SettingsPage {
]
}
- fn experimental_section() -> [SettingsPageItem; 9] {
+ fn audio_settings() -> [SettingsPageItem; 3] {
[
- SettingsPageItem::SectionHeader("Experimental"),
- SettingsPageItem::SettingItem(SettingItem {
- title: "Rodio Audio",
- description: "Opt into the new audio system.",
- field: Box::new(SettingField {
- json_path: Some("audio.experimental.rodio_audio"),
- pick: |settings_content| settings_content.audio.as_ref()?.rodio_audio.as_ref(),
- write: |settings_content, value| {
- settings_content.audio.get_or_insert_default().rodio_audio = value;
- },
- }),
- metadata: None,
- files: USER,
- }),
- SettingsPageItem::SettingItem(SettingItem {
- title: "Auto Microphone Volume",
- description: "Automatically adjust microphone volume (requires rodio audio).",
- field: Box::new(SettingField {
- json_path: Some("audio.experimental.auto_microphone_volume"),
- pick: |settings_content| {
- settings_content
- .audio
- .as_ref()?
- .auto_microphone_volume
- .as_ref()
- },
- write: |settings_content, value| {
- settings_content
- .audio
- .get_or_insert_default()
- .auto_microphone_volume = value;
- },
- }),
- metadata: None,
- files: USER,
- }),
- SettingsPageItem::SettingItem(SettingItem {
- title: "Auto Speaker Volume",
- description: "Automatically adjust volume of other call members (requires rodio audio).",
- field: Box::new(SettingField {
- json_path: Some("audio.experimental.auto_speaker_volume"),
- pick: |settings_content| {
- settings_content
- .audio
- .as_ref()?
- .auto_speaker_volume
- .as_ref()
- },
- write: |settings_content, value| {
- settings_content
- .audio
- .get_or_insert_default()
- .auto_speaker_volume = value;
- },
- }),
- metadata: None,
- files: USER,
- }),
- SettingsPageItem::SettingItem(SettingItem {
- title: "Denoise",
- description: "Remove background noises (requires rodio audio).",
- field: Box::new(SettingField {
- json_path: Some("audio.experimental.denoise"),
- pick: |settings_content| settings_content.audio.as_ref()?.denoise.as_ref(),
- write: |settings_content, value| {
- settings_content.audio.get_or_insert_default().denoise = value;
- },
- }),
- metadata: None,
- files: USER,
- }),
- SettingsPageItem::SettingItem(SettingItem {
- title: "Legacy Audio Compatible",
- description: "Use audio parameters compatible with previous versions (requires rodio audio).",
- field: Box::new(SettingField {
- json_path: Some("audio.experimental.legacy_audio_compatible"),
- pick: |settings_content| {
- settings_content
- .audio
- .as_ref()?
- .legacy_audio_compatible
- .as_ref()
- },
- write: |settings_content, value| {
- settings_content
- .audio
- .get_or_insert_default()
- .legacy_audio_compatible = value;
- },
- }),
- metadata: None,
- files: USER,
- }),
SettingsPageItem::ActionLink(ActionLink {
title: "Test Audio".into(),
description: Some("Test your microphone and speaker setup".into()),
@@ -7193,7 +7100,7 @@ fn collaboration_page() -> SettingsPage {
SettingsPage {
title: "Collaboration",
- items: concat_sections![calls_section(), experimental_section()],
+ items: concat_sections![calls_section(), audio_settings()],
}
}
@@ -17,7 +17,7 @@ use agent_ui::{AgentDiffToolbar, AgentPanelDelegate};
use anyhow::Context as _;
pub use app_menus::*;
use assets::Assets;
-use audio::{AudioSettings, REPLAY_DURATION};
+
use breadcrumbs::Breadcrumbs;
use client::zed_urls;
use collections::VecDeque;
@@ -69,7 +69,7 @@ use settings::{
update_settings_file,
};
use sidebar::Sidebar;
-use std::time::Duration;
+
use std::{
borrow::Cow,
path::{Path, PathBuf},
@@ -84,9 +84,7 @@ use util::rel_path::RelPath;
use util::{ResultExt, asset_str, maybe};
use uuid::Uuid;
use vim_mode_setting::VimModeSetting;
-use workspace::notifications::{
- NotificationId, SuppressEvent, dismiss_app_notification, show_app_notification,
-};
+use workspace::notifications::{NotificationId, dismiss_app_notification, show_app_notification};
use workspace::{
AppState, MultiWorkspace, NewFile, NewWindow, OpenLog, Panel, Toast, Workspace,
@@ -94,8 +92,7 @@ use workspace::{
notifications::simple_message_notification::MessageNotification, open_new,
};
use workspace::{
- CloseIntent, CloseProject, CloseWindow, NotificationFrame, RestoreBanner,
- with_active_or_new_workspace,
+ CloseIntent, CloseProject, CloseWindow, RestoreBanner, with_active_or_new_workspace,
};
use workspace::{Pane, notifications::DetachAndPromptErr};
use zed_actions::{
@@ -144,10 +141,6 @@ actions!(
actions!(
dev,
[
- /// Stores last 30s of audio from zed staff using the experimental rodio
- /// audio system (including yourself) on the current call in a tar file
- /// in the current working directory.
- CaptureRecentAudio,
/// Opens a prompt to enter a URL to open.
OpenUrlPrompt,
]
@@ -1148,9 +1141,6 @@ fn register_actions(
.detach_and_log_err(cx);
}
}
- })
- .register_action(|workspace, _: &CaptureRecentAudio, window, cx| {
- capture_recent_audio(workspace, window, cx);
});
#[cfg(not(target_os = "windows"))]
@@ -2141,84 +2131,6 @@ fn open_settings_file(
.detach_and_log_err(cx);
}
-fn capture_recent_audio(workspace: &mut Workspace, _: &mut Window, cx: &mut Context<Workspace>) {
- struct CaptureRecentAudioNotification {
- focus_handle: gpui::FocusHandle,
- save_result: Option<Result<(PathBuf, Duration), anyhow::Error>>,
- _save_task: Task<anyhow::Result<()>>,
- }
-
- impl gpui::EventEmitter<DismissEvent> for CaptureRecentAudioNotification {}
- impl gpui::EventEmitter<SuppressEvent> for CaptureRecentAudioNotification {}
- impl gpui::Focusable for CaptureRecentAudioNotification {
- fn focus_handle(&self, _cx: &App) -> gpui::FocusHandle {
- self.focus_handle.clone()
- }
- }
- impl workspace::notifications::Notification for CaptureRecentAudioNotification {}
-
- impl Render for CaptureRecentAudioNotification {
- fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
- let message = match &self.save_result {
- None => format!(
- "Saving up to {} seconds of recent audio",
- REPLAY_DURATION.as_secs(),
- ),
- Some(Ok((path, duration))) => format!(
- "Saved {} seconds of all audio to {}",
- duration.as_secs(),
- path.display(),
- ),
- Some(Err(e)) => format!("Error saving audio replays: {e:?}"),
- };
-
- NotificationFrame::new()
- .with_title(Some("Saved Audio"))
- .show_suppress_button(false)
- .on_close(cx.listener(|_, _, _, cx| {
- cx.emit(DismissEvent);
- }))
- .with_content(message)
- }
- }
-
- impl CaptureRecentAudioNotification {
- fn new(cx: &mut Context<Self>) -> Self {
- if AudioSettings::get_global(cx).rodio_audio {
- let executor = cx.background_executor().clone();
- let save_task = cx.default_global::<audio::Audio>().save_replays(executor);
- let _save_task = cx.spawn(async move |this, cx| {
- let res = save_task.await;
- this.update(cx, |this, cx| {
- this.save_result = Some(res);
- cx.notify();
- })
- });
-
- Self {
- focus_handle: cx.focus_handle(),
- _save_task,
- save_result: None,
- }
- } else {
- Self {
- focus_handle: cx.focus_handle(),
- _save_task: Task::ready(Ok(())),
- save_result: Some(Err(anyhow::anyhow!(
- "Capturing recent audio is only supported on the experimental rodio audio pipeline"
- ))),
- }
- }
- }
- }
-
- workspace.show_notification(
- NotificationId::unique::<CaptureRecentAudioNotification>(),
- cx,
- |cx| cx.new(CaptureRecentAudioNotification::new),
- );
-}
-
/// Eagerly loads the active theme and icon theme based on the selections in the
/// theme settings.
///