Skip to content

Commit

Permalink
Add back option for muting PC speakers
Browse files Browse the repository at this point in the history
  • Loading branch information
zmerp committed Jan 28, 2021
1 parent dae1b36 commit 3dbbd31
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 68 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions alvr/client/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ async fn connection_pipeline(
let (_destroy_stream_park_notifier, destroy_stream_park_receiver) = smpsc::channel::<()>();

// blocking streams park
std::thread::spawn(move || -> StrResult {
std::thread::spawn(move || {
let mut _game_audio_stream_guard = if let Switch::Enabled(desc) = settings.audio.game_audio
{
Some(audio::AudioPlayer::start(
Expand All @@ -369,13 +369,11 @@ async fn connection_pipeline(
// notified when the notifier counterpart gets dropped
destroy_stream_park_receiver.recv().ok();

error!("drop streams park");

// the microphone gets stuck on stop(), drop the game audio stream first
drop(_game_audio_stream_guard);
drop(_microphone_stream_guard);

Ok(())
StrResult::Ok(())
});

let keepalive_sender_loop = {
Expand Down
2 changes: 2 additions & 0 deletions alvr/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ runas = "0.2"
msgbox = "0.6"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["mmdeviceapi", "objbase", "endpointvolume"] }
wio = "0.2"
gfx-backend-dx11 = "0.6"

[target.'cfg(target_os = "linux")'.dependencies]
Expand Down
98 changes: 98 additions & 0 deletions alvr/common/src/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,104 @@ use cpal::{
use std::{collections::VecDeque, sync::mpsc as smpsc};
use tokio::sync::mpsc as tmpsc;

#[cfg(windows)]
use std::ptr;
#[cfg(windows)]
use winapi::{
shared::winerror::*,
um::{combaseapi::*, endpointvolume::IAudioEndpointVolume, mmdeviceapi::*, objbase::*},
Class, Interface,
};
#[cfg(windows)]
use wio::com::ComPtr;

#[cfg(windows)]
pub fn set_mute_audio_device(device_index: Option<u64>, mute: bool) -> StrResult {
unsafe {
CoInitializeEx(ptr::null_mut(), COINIT_MULTITHREADED);

let mut mm_device_enumerator_ptr: *mut IMMDeviceEnumerator = ptr::null_mut();
let hr = CoCreateInstance(
&MMDeviceEnumerator::uuidof(),
ptr::null_mut(),
CLSCTX_ALL,
&IMMDeviceEnumerator::uuidof(),
&mut mm_device_enumerator_ptr as *mut _ as _,
);
if FAILED(hr) {
return fmt_e!(
"CoCreateInstance(IMMDeviceEnumerator) failed: hr = 0x{:08x}",
hr
);
}
let mm_device_enumerator = ComPtr::from_raw(mm_device_enumerator_ptr);

let mm_device = if let Some(index) = device_index {
let mut mm_device_collection_ptr: *mut IMMDeviceCollection = ptr::null_mut();
let hr = mm_device_enumerator.EnumAudioEndpoints(
eRender,
DEVICE_STATE_ACTIVE,
&mut mm_device_collection_ptr as _,
);
if FAILED(hr) {
return fmt_e!(
"IMMDeviceEnumerator::EnumAudioEndpoints failed: hr = 0x{:08x}",
hr
);
}
let mm_device_collection = ComPtr::from_raw(mm_device_collection_ptr);

let mut mm_device_ptr: *mut IMMDevice = ptr::null_mut();
let hr = mm_device_collection.Item(index as _, &mut mm_device_ptr as _);
if FAILED(hr) {
return fmt_e!("IMMDeviceCollection::Item failed: hr = 0x{:08x}", hr);
}

ComPtr::from_raw(mm_device_ptr)
} else {
let mut mm_device_ptr: *mut IMMDevice = ptr::null_mut();
let hr = mm_device_enumerator.GetDefaultAudioEndpoint(
eRender,
eConsole,
&mut mm_device_ptr as *mut _,
);
if hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND) {
return fmt_e!("No default audio endpoint found. No audio device?");
}
if FAILED(hr) {
return fmt_e!(
"IMMDeviceEnumerator::GetDefaultAudioEndpoint failed: hr = 0x{:08x}",
hr
);
}

ComPtr::from_raw(mm_device_ptr)
};

let mut endpoint_volume_ptr: *mut IAudioEndpointVolume = ptr::null_mut();
let hr = mm_device.Activate(
&IAudioEndpointVolume::uuidof(),
CLSCTX_ALL,
ptr::null_mut(),
&mut endpoint_volume_ptr as *mut _ as _,
);
if FAILED(hr) {
return fmt_e!(
"IMMDevice::Activate() for IAudioEndpointVolume failed: hr = 0x{:08x}",
hr,
);
}
let endpoint_volume = ComPtr::from_raw(endpoint_volume_ptr);

let hr = endpoint_volume.SetMute(mute as _, ptr::null_mut());
if FAILED(hr) {
return fmt_e!("Failed to mute audio device: hr = 0x{:08x}", hr,);
}
}

Ok(())
}

pub fn get_vb_cable_audio_device_index() -> StrResult<Option<u64>> {
let host = cpal::default_host();

Expand Down
127 changes: 63 additions & 64 deletions alvr/server/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::{
};
use tokio::{
sync::{mpsc as tmpsc, Mutex},
task, time,
time,
};

const RETRY_CONNECT_INTERVAL: Duration = Duration::from_millis(500);
Expand Down Expand Up @@ -332,12 +332,9 @@ impl Drop for StreamCloseGuard {
crate::DeinitializeStreaming()
};

let on_disconnect_script = SESSION_MANAGER
.lock()
.get()
.to_settings()
.connection
.on_disconnect_script;
let settings = SESSION_MANAGER.lock().get().to_settings();

let on_disconnect_script = settings.connection.on_disconnect_script;
if !on_disconnect_script.is_empty() {
info!(
"Running on disconnect script (disconnect): {}",
Expand Down Expand Up @@ -402,67 +399,74 @@ async fn connection_pipeline() -> StrResult {

let _stream_guard = StreamCloseGuard;

let (_destroy_game_audio_stream_notifier, destroy_game_audio_stream_receiver) =
smpsc::channel::<()>();

let game_audio_loop: BoxFuture<_> = if let Switch::Enabled(desc) = settings.audio.game_audio {
let (sender, mut receiver) = tmpsc::unbounded_channel();
let (game_audio_sender, mut game_audio_receiver) = tmpsc::unbounded_channel();
let game_audio_loop: BoxFuture<_> = if let Switch::Enabled(_) = settings.audio.game_audio {
let control_sender = control_sender.clone();
Box::pin(async move {
while let Some(data) = game_audio_receiver.recv().await {
control_sender
.lock()
.await
.send(&ServerControlPacket::ReservedBuffer(data))
.await?;
}

// AudioSession is !Send, so keep it in a separate thread.
Box::pin(futures::future::join(
task::spawn_blocking(move || {
let _audio_stream_guard = Some(AudioSession::start_recording(
StrResult::Ok(())
})
} else {
Box::pin(future::pending())
};

let (_destroy_stream_park_notifier, destroy_stream_park_receiver) = smpsc::channel::<()>();
let (microphone_sender, microphone_receiver) = smpsc::channel();
std::thread::spawn({
let game_audio_desc = settings.audio.game_audio;
let microphone_desc = settings.audio.microphone;
move || {
let mut maybe_muted_device_index = None;
let _audio_stream_guard = if let Switch::Enabled(desc) = game_audio_desc {
#[cfg(windows)]
if desc.mute_when_streaming {
audio::set_mute_audio_device(desc.device_index, true)?;
maybe_muted_device_index = Some(desc.device_index);
}

Some(AudioSession::start_recording(
desc.device_index,
true,
2,
trace_err!(audio::get_output_sample_rate(desc.device_index))?,
sender,
)?);

// notified when _destroy_game_audio_stream_notifier goes out of scope
destroy_game_audio_stream_receiver.recv().ok();
StrResult::Ok(())
}),
async move {
while let Some(data) = receiver.recv().await {
control_sender
.lock()
.await
.send(&ServerControlPacket::ReservedBuffer(data))
.await?;
}
game_audio_sender,
)?)
} else {
None
};

StrResult::Ok(())
},
))
} else {
Box::pin(future::pending())
};
let _microphone_stream_guard = if let Switch::Enabled(desc) = microphone_desc {
let device_index = desc
.device_index
.or(trace_err!(audio::get_vb_cable_audio_device_index())?);
Some(AudioSession::start_playing(
device_index,
1,
trace_err!(audio::get_output_sample_rate(device_index))?,
desc.buffer_range_multiplier,
microphone_receiver,
)?)
} else {
None
};

let (_destroy_microphone_stream_notifier, destroy_microphone_stream_receiver) =
smpsc::channel::<()>();
let (microphone_sender, microphone_receiver) = smpsc::channel();
destroy_stream_park_receiver.recv().ok();

#[cfg(windows)]
if let Some(index) = maybe_muted_device_index {
audio::set_mute_audio_device(index, false)?;
}

let microphone_stream: BoxFuture<_> = if let Switch::Enabled(desc) = settings.audio.microphone {
Box::pin(task::spawn_blocking(move || {
let device_index = desc
.device_index
.or(trace_err!(audio::get_vb_cable_audio_device_index())?);
let _audio_stream_guard = Some(AudioSession::start_playing(
device_index,
1,
trace_err!(audio::get_output_sample_rate(device_index))?,
desc.buffer_range_multiplier,
microphone_receiver,
)?);

destroy_microphone_stream_receiver.recv().ok();
StrResult::Ok(())
}))
} else {
Box::pin(future::pending())
};
}
});

let keepalive_sender_loop = {
let control_sender = control_sender.clone();
Expand Down Expand Up @@ -536,12 +540,7 @@ async fn connection_pipeline() -> StrResult {

Ok(())
}
res = microphone_stream => trace_err!(res)?,
(res1, res2) = game_audio_loop => {
trace_err!(res1)??;
res2?;
Ok(())
}
res = game_audio_loop => res,
res = keepalive_sender_loop => res,
res = control_loop => res,
}
Expand Down

0 comments on commit 3dbbd31

Please sign in to comment.