From f5ff14207bc0d97fa7c9eb482e1ab00b15947f70 Mon Sep 17 00:00:00 2001
From: Ben Wallis <atomyc@gmail.com>
Date: Sun, 7 Jun 2020 16:00:31 +0100
Subject: [PATCH] Fix #322 - Refactored audio output device enumeration to fail
 gracefully when audio devices with null or otherwise unusable names are
 encountered

---
 CHANGELOG.md             |  1 +
 voxygen/src/audio/mod.rs | 26 ++++++++++++++++++--------
 2 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2eba85b7c1..626d356555 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - new tail bone for quad_small body
 - slim the game size through lossless asset optimization
 - Lanterns now stop glowing if you throw a lit one out of your inventory
+- Fixed a crash caused by certain audio devices on OSX
 
 ### Removed
 
diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs
index 299a8c53fe..3205a6c991 100644
--- a/voxygen/src/audio/mod.rs
+++ b/voxygen/src/audio/mod.rs
@@ -239,7 +239,10 @@ impl AudioFrontend {
 /// Returns the default audio device.
 /// Does not return rodio Device struct in case our audio backend changes.
 pub fn get_default_device() -> Option<String> {
-    rodio::default_output_device().map(|dev| dev.name().expect("Unable to get device name"))
+    match rodio::default_output_device() {
+        Some(x) => Some(x.name().ok()?),
+        None => None,
+    }
 }
 
 /// Returns a vec of the audio devices available.
@@ -247,19 +250,26 @@ pub fn get_default_device() -> Option<String> {
 pub fn list_devices() -> Vec<String> {
     list_devices_raw()
         .iter()
-        .map(|x| x.name().expect("Unable to get device name"))
+        .map(|x| x.name().unwrap())
         .collect()
 }
 
 /// Returns vec of devices
 fn list_devices_raw() -> Vec<Device> {
-    rodio::output_devices()
-        .expect("Unable to get output devices")
-        .collect()
+    match rodio::output_devices() {
+        Ok(devices) => {
+            // Filter out any devices that the name isn't available for
+            devices.filter(|d| d.name().is_ok()).collect()
+        },
+        Err(_) => {
+            log::warn!("Failed to enumerate audio output devices, audio will not be available");
+            Vec::new()
+        },
+    }
 }
 
 fn get_device_raw(device: &str) -> Option<Device> {
-    rodio::output_devices()
-        .expect("Unable to get output devices")
-        .find(|d| d.name().expect("Unable to get device name") == device)
+    list_devices_raw()
+        .into_iter()
+        .find(|d| d.name().unwrap() == device)
 }