Detailed changes
@@ -384,17 +384,29 @@ pub fn open_input_stream(
Ok(stream)
}
-pub fn open_output_stream(device_id: Option<DeviceId>) -> anyhow::Result<MixerDeviceSink> {
- let output_handle = if let Some(id) = device_id {
- if let Some(device) = default_host().device_by_id(&id) {
- DeviceSinkBuilder::from_device(device)?.open_stream()
- } else {
- DeviceSinkBuilder::open_default_sink()
+pub fn resolve_device(device_id: Option<&DeviceId>, input: bool) -> anyhow::Result<cpal::Device> {
+ if let Some(id) = device_id {
+ if let Some(device) = default_host().device_by_id(id) {
+ return Ok(device);
}
+ log::warn!("Selected audio device not found, falling back to default");
+ }
+ if input {
+ default_host()
+ .default_input_device()
+ .context("no audio input device available")
} else {
- DeviceSinkBuilder::open_default_sink()
- };
- let mut output_handle = output_handle.context("Could not open output stream")?;
+ default_host()
+ .default_output_device()
+ .context("no audio output device available")
+ }
+}
+
+pub fn open_output_stream(device_id: Option<DeviceId>) -> anyhow::Result<MixerDeviceSink> {
+ let device = resolve_device(device_id.as_ref(), false)?;
+ let mut output_handle = DeviceSinkBuilder::from_device(device)?
+ .open_stream()
+ .context("Could not open output stream")?;
output_handle.log_on_drop(false);
log::info!("Output stream: {:?}", output_handle);
Ok(output_handle)
@@ -42,12 +42,8 @@ pub struct AudioSettings {
///
/// You need to rejoin a call for this setting to apply
pub legacy_audio_compatible: bool,
- /// Requires 'rodio_audio: true'
- ///
/// Select specific output audio device.
pub output_audio_device: Option<DeviceId>,
- /// Requires 'rodio_audio: true'
- ///
/// Select specific input audio device.
pub input_audio_device: Option<DeviceId>,
}
@@ -1,8 +1,8 @@
use anyhow::Context as _;
use collections::HashMap;
+use cpal::DeviceId;
mod remote_video_track_view;
-use cpal::traits::HostTrait as _;
pub use remote_video_track_view::{RemoteVideoTrackView, RemoteVideoTrackViewEvent};
use rodio::DeviceTrait as _;
@@ -192,24 +192,18 @@ pub enum RoomEvent {
pub(crate) fn default_device(
input: bool,
+ device_id: Option<&DeviceId>,
) -> anyhow::Result<(cpal::Device, cpal::SupportedStreamConfig)> {
- let device;
- let config;
- if input {
- device = cpal::default_host()
- .default_input_device()
- .context("no audio input device available")?;
- config = device
+ let device = audio::resolve_device(device_id, input)?;
+ let config = if input {
+ device
.default_input_config()
- .context("failed to get default input config")?;
+ .context("failed to get default input config")?
} else {
- device = cpal::default_host()
- .default_output_device()
- .context("no audio output device available")?;
- config = device
+ device
.default_output_config()
- .context("failed to get default output config")?;
- }
+ .context("failed to get default output config")?
+ };
Ok((device, config))
}
@@ -150,7 +150,10 @@ impl Room {
info!("Using experimental.rodio_audio audio pipeline for output");
playback::play_remote_audio_track(&track.0, speaker, cx)
} else if speaker.sends_legacy_audio {
- Ok(self.playback.play_remote_audio_track(&track.0))
+ 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"))
}
@@ -1,6 +1,7 @@
use anyhow::{Context as _, Result};
use audio::{AudioSettings, CHANNEL_COUNT, LEGACY_CHANNEL_COUNT, LEGACY_SAMPLE_RATE, SAMPLE_RATE};
+use cpal::DeviceId;
use cpal::traits::{DeviceTrait, StreamTrait as _};
use futures::channel::mpsc::UnboundedSender;
use futures::{Stream, StreamExt as _};
@@ -91,8 +92,9 @@ impl AudioStack {
pub(crate) fn play_remote_audio_track(
&self,
track: &livekit::track::RemoteAudioTrack,
+ output_audio_device: Option<DeviceId>,
) -> AudioStream {
- let output_task = self.start_output();
+ let output_task = self.start_output(output_audio_device);
let next_ssrc = self.next_ssrc.fetch_add(1, Ordering::Relaxed);
let source = AudioMixerSource {
@@ -130,7 +132,7 @@ impl AudioStack {
}
}
- fn start_output(&self) -> Arc<Task<()>> {
+ fn start_output(&self, output_audio_device: Option<DeviceId>) -> Arc<Task<()>> {
if let Some(task) = self._output_task.borrow().upgrade() {
return task;
}
@@ -143,6 +145,7 @@ impl AudioStack {
mixer,
LEGACY_SAMPLE_RATE.get(),
LEGACY_CHANNEL_COUNT.get().into(),
+ output_audio_device,
)
.await
.log_err();
@@ -219,12 +222,16 @@ impl AudioStack {
Ok(())
})
} else {
+ let input_audio_device =
+ AudioSettings::try_read_global(cx, |settings| settings.input_audio_device.clone())
+ .flatten();
self.executor.spawn(async move {
Self::capture_input(
apm,
frame_tx,
LEGACY_SAMPLE_RATE.get(),
LEGACY_CHANNEL_COUNT.get().into(),
+ input_audio_device,
)
.await
})
@@ -247,6 +254,7 @@ impl AudioStack {
mixer: Arc<Mutex<audio_mixer::AudioMixer>>,
sample_rate: u32,
num_channels: u32,
+ output_audio_device: Option<DeviceId>,
) -> Result<()> {
// Prevent App Nap from throttling audio playback on macOS.
// This guard is held for the entire duration of audio output.
@@ -255,7 +263,8 @@ impl AudioStack {
loop {
let mut device_change_listener = DeviceChangeListener::new(false)?;
- let (output_device, output_config) = crate::default_device(false)?;
+ let (output_device, output_config) =
+ crate::default_device(false, output_audio_device.as_ref())?;
let (end_on_drop_tx, end_on_drop_rx) = std::sync::mpsc::channel::<()>();
let mixer = mixer.clone();
let apm = apm.clone();
@@ -327,10 +336,11 @@ impl AudioStack {
frame_tx: UnboundedSender<AudioFrame<'static>>,
sample_rate: u32,
num_channels: u32,
+ input_audio_device: Option<DeviceId>,
) -> Result<()> {
loop {
let mut device_change_listener = DeviceChangeListener::new(true)?;
- let (device, config) = crate::default_device(true)?;
+ let (device, config) = crate::default_device(true, input_audio_device.as_ref())?;
let (end_on_drop_tx, end_on_drop_rx) = std::sync::mpsc::channel::<()>();
let apm = apm.clone();
let frame_tx = frame_tx.clone();
@@ -7,20 +7,22 @@ use std::{
};
use anyhow::{Context, Result};
+use cpal::DeviceId;
use cpal::traits::{DeviceTrait, StreamTrait};
use rodio::{buffer::SamplesBuffer, conversions::SampleTypeConverter};
use util::ResultExt;
pub struct CaptureInput {
pub name: String,
+ pub input_device: Option<DeviceId>,
config: cpal::SupportedStreamConfig,
samples: Arc<Mutex<Vec<i16>>>,
_stream: cpal::Stream,
}
impl CaptureInput {
- pub fn start() -> anyhow::Result<Self> {
- let (device, config) = crate::default_device(true)?;
+ pub fn start(input_device: Option<DeviceId>) -> anyhow::Result<Self> {
+ let (device, config) = crate::default_device(true, input_device.as_ref())?;
let name = device
.description()
.map(|desc| desc.name().to_string())
@@ -32,6 +34,7 @@ impl CaptureInput {
Ok(Self {
name,
+ input_device,
_stream: stream,
config,
samples,