33
44#nullable disable
55
6+ using System . Collections . Generic ;
7+ using System . Linq ;
8+ using osu . Framework ;
69using osu . Framework . Allocation ;
710using osu . Framework . Audio ;
11+ using osu . Framework . Configuration ;
812using osu . Framework . Graphics ;
9- using System . Collections . Generic ;
10- using System . Linq ;
1113using osu . Framework . Localisation ;
14+ using osu . Framework . Platform ;
15+ using osu . Game . Configuration ;
1216using osu . Game . Graphics . UserInterface ;
1317using osu . Game . Localisation ;
18+ using osu . Game . Overlays . Dialog ;
1419
1520namespace osu . Game . Overlays . Settings . Sections . Audio
1621{
@@ -21,49 +26,93 @@ public partial class AudioDevicesSettings : SettingsSubsection
2126 [ Resolved ]
2227 private AudioManager audio { get ; set ; }
2328
24- private SettingsDropdown < string > dropdown ;
29+ private SettingsDropdown < AudioBackend > backend ;
30+ private SettingsDropdown < string > device ;
31+ private SettingsDropdown < AudioExclusiveModeBehaviour > exclusiveModeBehaviour ;
32+
33+ private bool automaticBackendInUse ;
2534
2635 [ BackgroundDependencyLoader ]
27- private void load ( )
36+ private void load ( FrameworkConfigManager config , OsuGame game , IDialogOverlay dialogOverlay )
2837 {
38+ var currentBackend = config . GetBindable < AudioBackend > ( FrameworkSetting . AudioBackend ) ;
39+ var currentExclusiveModeBehaviour = localConfig . GetBindable < AudioExclusiveModeBehaviour > ( FrameworkSetting . AudioExclusiveModeBehaviour ) ;
40+ // Audio backend selection is currently only available on Windows for now.
41+ currentBackend . Disabled = RuntimeInfo . OS != RuntimeInfo . Platform . Windows ;
42+ currentExclusiveModeBehaviour . Disabled = RuntimeInfo . OS != RuntimeInfo . Platform . Windows ;
43+ automaticBackendInUse = currentBackend . Value == AudioBackend . Automatic ;
44+
2945 Children = new Drawable [ ]
3046 {
31- dropdown = new AudioDeviceSettingsDropdown
47+ backend = new SettingsEnumDropdown < AudioBackend >
48+ {
49+ LabelText = AudioSettingsStrings . AudioBackend ,
50+ Keywords = new [ ] { "backend" , "bass" , "wasapi" } ,
51+ Current = currentBackend ,
52+ Items = game . GetPreferredAudioBackendsForCurrentPlatform ( ) ,
53+ } ,
54+ device = new AudioDeviceSettingsDropdown
3255 {
3356 LabelText = AudioSettingsStrings . OutputDevice ,
34- Keywords = new [ ] { "speaker" , "headphone" , "output" }
35- }
57+ Keywords = new [ ] { "speaker" , "headphone" , "output" } ,
58+ } ,
59+ exclusiveModeBehaviour = new SettingsEnumDropdown < AudioExclusiveModeBehaviour >
60+ {
61+ LabelText = AudioSettingsStrings . ExclusiveModeBehaviour ,
62+ Keywords = new [ ] { "exclusive" , "latency" } ,
63+ Current = currentExclusiveModeBehaviour ,
64+ } ,
3665 } ;
3766
67+ currentBackend . BindValueChanged ( r =>
68+ {
69+ if ( r . NewValue != AudioBackend . BassWasapi )
70+ exclusiveModeBehaviour . SetNoticeText ( AudioSettingsStrings . ExclusiveModeAvailabilityNote , true ) ;
71+ else
72+ exclusiveModeBehaviour . ClearNoticeText ( ) ;
73+
74+ // Up to here is executed immediately.
75+ if ( r . OldValue == r . NewValue )
76+ return ;
77+
78+ if ( r . NewValue == game . ResolvedAudioBackend )
79+ return ;
80+
81+ if ( r . NewValue == AudioBackend . Automatic && automaticBackendInUse )
82+ return ;
83+
84+ if ( game ? . RestartAppWhenExited ( ) == true )
85+ {
86+ game . AttemptExit ( ) ;
87+ }
88+ else
89+ {
90+ dialogOverlay ? . Push ( new ConfirmDialog ( AudioSettingsStrings . ChangeAudioBackendConfirmation , ( ) => game ? . AttemptExit ( ) , ( ) =>
91+ {
92+ currentBackend . Value = automaticBackendInUse ? AudioBackend . Automatic : game ? . ResolvedAudioBackend ?? AudioBackend . Automatic ;
93+ } ) ) ;
94+ }
95+ } , true ) ;
96+
3897 updateItems ( ) ;
3998
4099 audio . OnNewDevice += onDeviceChanged ;
41100 audio . OnLostDevice += onDeviceChanged ;
42- dropdown . Current = audio . AudioDevice ;
101+ device . Current = audio . AudioDevice ;
43102 }
44103
45- private void onDeviceChanged ( string name ) => updateItems ( ) ;
104+ private void onDeviceChanged ( KeyValuePair < string , string > _ ) => updateItems ( ) ;
46105
47106 private void updateItems ( )
48107 {
49108 var deviceItems = new List < string > { string . Empty } ;
50- deviceItems . AddRange ( audio . AudioDeviceNames ) ;
109+ deviceItems . AddRange ( audio . AudioDevices . Keys ) ;
51110
52111 string preferredDeviceName = audio . AudioDevice . Value ;
53112 if ( deviceItems . All ( kv => kv != preferredDeviceName ) )
54113 deviceItems . Add ( preferredDeviceName ) ;
55114
56- // The option dropdown for audio device selection lists all audio
57- // device names. Dropdowns, however, may not have multiple identical
58- // keys. Thus, we remove duplicate audio device names from
59- // the dropdown. BASS does not give us a simple mechanism to select
60- // specific audio devices in such a case anyways. Such
61- // functionality would require involved OS-specific code.
62- dropdown . Items = deviceItems
63- // Dropdown doesn't like null items. Somehow we are seeing some arrive here (see https://github.com/ppy/osu/issues/21271)
64- . Where ( i => i != null )
65- . Distinct ( )
66- . ToList ( ) ;
115+ device . Items = deviceItems . ToList ( ) ;
67116 }
68117
69118 protected override void Dispose ( bool isDisposing )
@@ -83,8 +132,15 @@ private partial class AudioDeviceSettingsDropdown : SettingsDropdown<string>
83132
84133 private partial class AudioDeviceDropdownControl : DropdownControl
85134 {
135+ [ Resolved ]
136+ private AudioManager audio { get ; set ; }
137+
86138 protected override LocalisableString GenerateItemText ( string item )
87- => string . IsNullOrEmpty ( item ) ? CommonStrings . Default : base . GenerateItemText ( item ) ;
139+ => string . IsNullOrEmpty ( item )
140+ ? CommonStrings . Default
141+ : audio is AudioManager manager && manager . AudioDevices . TryGetValue ( item , out string value )
142+ ? value
143+ : item ;
88144 }
89145 }
90146 }
0 commit comments