From c82d1b9316900e955320b79b3a33c654003b37b5 Mon Sep 17 00:00:00 2001
From: Marli Frost <marli@frost.red>
Date: Sat, 28 Mar 2020 13:12:35 +0000
Subject: [PATCH 1/3] allow the game to start without an audio device

---
 voxygen/src/audio/mod.rs |  7 ++-----
 voxygen/src/main.rs      | 17 +++++++++--------
 2 files changed, 11 insertions(+), 13 deletions(-)

diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs
index 1709649c89..299a8c53fe 100644
--- a/voxygen/src/audio/mod.rs
+++ b/voxygen/src/audio/mod.rs
@@ -238,11 +238,8 @@ impl AudioFrontend {
 
 /// Returns the default audio device.
 /// Does not return rodio Device struct in case our audio backend changes.
-pub fn get_default_device() -> String {
-    rodio::default_output_device()
-        .expect("No audio output devices detected.")
-        .name()
-        .expect("Unable to get device name")
+pub fn get_default_device() -> Option<String> {
+    rodio::default_output_device().map(|dev| dev.name().expect("Unable to get device name"))
 }
 
 /// Returns a vec of the audio devices available.
diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs
index 43b6726ab6..f27beab1ff 100644
--- a/voxygen/src/main.rs
+++ b/voxygen/src/main.rs
@@ -44,15 +44,16 @@ fn main() {
         panic!("Failed to save settings: {:?}", err);
     }
 
-    let audio_device = || match &settings.audio.audio_device {
-        Some(d) => d.to_string(),
-        None => audio::get_default_device(),
-    };
+    let audio_device = settings
+        .audio
+        .audio_device
+        .as_ref()
+        .map(|s| s.clone())
+        .or_else(audio::get_default_device);
 
-    let mut audio = if settings.audio.audio_on {
-        AudioFrontend::new(audio_device(), settings.audio.max_sfx_channels)
-    } else {
-        AudioFrontend::no_audio()
+    let mut audio = match (audio_device, settings.audio.audio_on) {
+        (Some(dev), true) => AudioFrontend::new(dev, settings.audio.max_sfx_channels),
+        _ => AudioFrontend::no_audio()
     };
 
     audio.set_music_volume(settings.audio.music_volume);

From a83f09bd606b8bc1154fc54d291a54336996cb93 Mon Sep 17 00:00:00 2001
From: Marli Frost <marli@frost.red>
Date: Tue, 31 Mar 2020 18:02:26 +0100
Subject: [PATCH 2/3] fix: avoid rodio API when audio is disabled

---
 voxygen/src/main.rs     | 24 ++++++++++++++----------
 voxygen/src/settings.rs |  5 +++++
 2 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs
index f27beab1ff..f874529d10 100644
--- a/voxygen/src/main.rs
+++ b/voxygen/src/main.rs
@@ -1,4 +1,5 @@
 #![deny(unsafe_code)]
+#![feature(bool_to_option)]
 #![recursion_limit = "2048"]
 
 use veloren_voxygen::{
@@ -44,17 +45,20 @@ fn main() {
         panic!("Failed to save settings: {:?}", err);
     }
 
-    let audio_device = settings
+    let mut audio = settings
         .audio
-        .audio_device
-        .as_ref()
-        .map(|s| s.clone())
-        .or_else(audio::get_default_device);
-
-    let mut audio = match (audio_device, settings.audio.audio_on) {
-        (Some(dev), true) => AudioFrontend::new(dev, settings.audio.max_sfx_channels),
-        _ => AudioFrontend::no_audio()
-    };
+        .audio_on
+        .then(|| {
+            settings
+                .audio
+                .audio_device
+                .as_ref()
+                .map(Clone::clone)
+                .or_else(audio::get_default_device)
+        })
+        .flatten()
+        .map(|dev| AudioFrontend::new(dev, settings.audio.max_sfx_channels))
+        .unwrap_or_else(AudioFrontend::no_audio);
 
     audio.set_music_volume(settings.audio.music_volume);
     audio.set_sfx_volume(settings.audio.sfx_volume);
diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs
index a8ce79e0b4..59583661e4 100644
--- a/voxygen/src/settings.rs
+++ b/voxygen/src/settings.rs
@@ -474,6 +474,11 @@ pub struct AudioSettings {
 
     /// Audio Device that Voxygen will use to play audio.
     pub audio_device: Option<String>,
+    /// Veloren's audio system wont work on some systems,
+    /// so you can use this to disable it, and allow the
+    /// game to function
+    // If this option is disabled, functions in the rodio
+    // library MUST NOT be called.
     pub audio_on: bool,
 }
 

From 3806f08eb00b51d78cd1ce9ff1dc38cc189b2591 Mon Sep 17 00:00:00 2001
From: Marli Frost <marli@frost.red>
Date: Tue, 31 Mar 2020 19:12:49 +0100
Subject: [PATCH 3/3] feat: configure audio output using an enum

---
 voxygen/src/main.rs          | 23 ++++++++---------------
 voxygen/src/menu/main/mod.rs |  2 +-
 voxygen/src/session.rs       |  3 ++-
 voxygen/src/settings.rs      | 30 +++++++++++++++++++++---------
 4 files changed, 32 insertions(+), 26 deletions(-)

diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs
index f874529d10..72382f5ecd 100644
--- a/voxygen/src/main.rs
+++ b/voxygen/src/main.rs
@@ -8,7 +8,7 @@ use veloren_voxygen::{
     logging,
     menu::main::MainMenuState,
     meta::Meta,
-    settings::Settings,
+    settings::{AudioOutput, Settings},
     window::Window,
     Direction, GlobalState, PlayState, PlayStateResult,
 };
@@ -45,20 +45,13 @@ fn main() {
         panic!("Failed to save settings: {:?}", err);
     }
 
-    let mut audio = settings
-        .audio
-        .audio_on
-        .then(|| {
-            settings
-                .audio
-                .audio_device
-                .as_ref()
-                .map(Clone::clone)
-                .or_else(audio::get_default_device)
-        })
-        .flatten()
-        .map(|dev| AudioFrontend::new(dev, settings.audio.max_sfx_channels))
-        .unwrap_or_else(AudioFrontend::no_audio);
+    let mut audio = match settings.audio.output {
+        AudioOutput::Off => None,
+        AudioOutput::Automatic => audio::get_default_device(),
+        AudioOutput::Device(ref dev) => Some(dev.clone()),
+    }
+    .map(|dev| AudioFrontend::new(dev, settings.audio.max_sfx_channels))
+    .unwrap_or_else(AudioFrontend::no_audio);
 
     audio.set_music_volume(settings.audio.music_volume);
     audio.set_sfx_volume(settings.audio.sfx_volume);
diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs
index 145d8e9b16..3e873f048d 100644
--- a/voxygen/src/menu/main/mod.rs
+++ b/voxygen/src/menu/main/mod.rs
@@ -36,7 +36,7 @@ impl PlayState for MainMenuState {
         let mut client_init: Option<ClientInit> = None;
 
         // Kick off title music
-        if global_state.settings.audio.audio_on && global_state.audio.music_enabled() {
+        if global_state.settings.audio.output.is_enabled() && global_state.audio.music_enabled() {
             global_state.audio.play_title_music();
         }
 
diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs
index 335b06b21d..2f1220c070 100644
--- a/voxygen/src/session.rs
+++ b/voxygen/src/session.rs
@@ -6,6 +6,7 @@ use crate::{
     menu::char_selection::CharSelectionState,
     render::Renderer,
     scene::{camera, Scene, SceneData},
+    settings::AudioOutput,
     window::{AnalogGameInput, Event, GameInput},
     Direction, Error, GlobalState, PlayState, PlayStateResult,
 };
@@ -613,7 +614,7 @@ impl PlayState for SessionState {
                     HudEvent::ChangeAudioDevice(name) => {
                         global_state.audio.set_device(name.clone());
 
-                        global_state.settings.audio.audio_device = Some(name);
+                        global_state.settings.audio.output = AudioOutput::Device(name);
                         global_state.settings.save_to_file_warn();
                     },
                     HudEvent::ChangeMaxFPS(fps) => {
diff --git a/voxygen/src/settings.rs b/voxygen/src/settings.rs
index 59583661e4..e28132f592 100644
--- a/voxygen/src/settings.rs
+++ b/voxygen/src/settings.rs
@@ -461,7 +461,26 @@ impl Default for GraphicsSettings {
         }
     }
 }
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub enum AudioOutput {
+    /// Veloren's audio system wont work on some systems,
+    /// so you can use this to disable it, and allow the
+    /// game to function
+    // If this option is disabled, functions in the rodio
+    // library MUST NOT be called.
+    Off,
+    Automatic,
+    Device(String),
+}
 
+impl AudioOutput {
+    pub fn is_enabled(&self) -> bool {
+        match self {
+            Self::Off => false,
+            _ => true,
+        }
+    }
+}
 /// `AudioSettings` controls the volume of different audio subsystems and which
 /// device is used.
 #[derive(Clone, Debug, Serialize, Deserialize)]
@@ -473,13 +492,7 @@ pub struct AudioSettings {
     pub max_sfx_channels: usize,
 
     /// Audio Device that Voxygen will use to play audio.
-    pub audio_device: Option<String>,
-    /// Veloren's audio system wont work on some systems,
-    /// so you can use this to disable it, and allow the
-    /// game to function
-    // If this option is disabled, functions in the rodio
-    // library MUST NOT be called.
-    pub audio_on: bool,
+    pub output: AudioOutput,
 }
 
 impl Default for AudioSettings {
@@ -489,8 +502,7 @@ impl Default for AudioSettings {
             music_volume: 0.4,
             sfx_volume: 0.6,
             max_sfx_channels: 10,
-            audio_device: None,
-            audio_on: true,
+            output: AudioOutput::Automatic,
         }
     }
 }