Skip to content

Commit 89290ba

Browse files
committed
Audio Device Switching Option
1 parent de5fe9b commit 89290ba

File tree

4 files changed

+117
-11
lines changed

4 files changed

+117
-11
lines changed

source/funkin/Preferences.hx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,38 @@ class Preferences
415415
return value;
416416
}
417417

418+
/**
419+
* What audio device should it playback sounds to.
420+
* @default Default
421+
*/
422+
public static var audioDevice(get, set):String;
423+
424+
static function get_audioDevice():String
425+
{
426+
return Save?.instance?.options?.audioDevice ?? "Default";
427+
}
428+
429+
static function set_audioDevice(value:String):String
430+
{
431+
if (value == Save.instance.options.audioDevice) return value;
432+
433+
var save:Save = Save.instance;
434+
if (value != "Default" && lime.media.AudioManager.refresh(value))
435+
{
436+
lime.media.AudioManager.automaticDefaultPlaybackDevice = false;
437+
}
438+
else
439+
{
440+
lime.media.AudioManager.refresh();
441+
lime.media.AudioManager.automaticDefaultPlaybackDevice = true;
442+
}
443+
444+
save.options.audioDevice = value;
445+
Save.system.flush();
446+
447+
return value;
448+
}
449+
418450
/**
419451
* If enabled, the game will hide the mouse when taking a screenshot.
420452
* @default `true`
@@ -484,6 +516,17 @@ class Preferences
484516
setDebugDisplayMode(Preferences.debugDisplay);
485517
setDebugDisplayBGOpacity(Preferences.debugDisplayBGOpacity / 100);
486518

519+
// Apply audio device preference, if failed, fallback to Default.
520+
if (lime.media.AudioManager.refresh(Preferences.audioDevice))
521+
{
522+
lime.media.AudioManager.automaticDefaultPlaybackDevice = false;
523+
}
524+
else
525+
{
526+
Preferences.audioDevice = "Default";
527+
lime.media.AudioManager.automaticDefaultPlaybackDevice = true;
528+
}
529+
487530
#if web
488531
toggleFramerateCap(Preferences.unlockedFramerate);
489532
#end

source/funkin/save/Save.hx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ class Save implements ConsoleClass
120120
globalOffset: 0,
121121
audioVisualOffset: 0,
122122
unlockedFramerate: false,
123+
audioDevice: 'Default',
123124
screenshot:
124125
{
125126
shouldHideMouse: true,
@@ -1232,6 +1233,12 @@ typedef SaveDataOptions =
12321233
*/
12331234
var unlockedFramerate:Bool;
12341235

1236+
/**
1237+
* What audio device should it playback sounds to.
1238+
* @default 'Default'
1239+
*/
1240+
var audioDevice:String;
1241+
12351242
/**
12361243
* Screenshot options
12371244
* @param shouldHideMouse Should the mouse be hidden when taking a screenshot? Default: `true`

source/funkin/ui/options/PreferencesMenu.hx

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import funkin.util.TouchUtil;
2525
import funkin.util.SwipeUtil;
2626
#end
2727
import funkin.util.HapticUtil;
28+
import lime.media.AudioManager;
2829
import lime.ui.WindowVSyncMode;
2930

3031
class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
@@ -39,6 +40,10 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
3940
var hudCamera:FlxCamera;
4041
var camFollow:FlxObject;
4142

43+
#if desktop
44+
var audioDeviceItem:EnumPreferenceItem<String>;
45+
#end
46+
4247
public function new()
4348
{
4449
super();
@@ -170,6 +175,17 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
170175
}, Preferences.autoFullscreen);
171176
#end
172177

178+
#if desktop
179+
audioDeviceItem = createPrefItemEnum('Audio Device', 'What audio device should the game playbacks sounds to.', generateAudioDeviceEnums(),
180+
(key:String, value:String) ->
181+
{
182+
Preferences.audioDevice = value;
183+
}, Preferences.audioDevice);
184+
AudioManager.onDefaultPlaybackDeviceChanged.add(refreshAudioDeviceItem);
185+
AudioManager.onPlaybackDeviceAdded.add(refreshAudioDeviceItem);
186+
AudioManager.onPlaybackDeviceRemoved.add(refreshAudioDeviceItem);
187+
#end
188+
173189
#if web
174190
createPrefItemCheckbox('Unlocked Framerate', 'When enabled, the framerate is unlocked.', function(value:Bool):Void {
175191
Preferences.unlockedFramerate = value;
@@ -260,7 +276,7 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
260276
* @param onChange Gets called every time the player changes the value; use this to apply the value
261277
* @param defaultValue The value that is loaded in when the pref item is created (usually your Preferences.settingVariable)
262278
*/
263-
function createPrefItemCheckbox(prefName:String, prefDesc:String, onChange:Bool->Void, defaultValue:Bool, available:Bool = true):Void
279+
function createPrefItemCheckbox(prefName:String, prefDesc:String, onChange:Bool->Void, defaultValue:Bool, available:Bool = true):CheckboxPreferenceItem
264280
{
265281
var checkbox:CheckboxPreferenceItem = new CheckboxPreferenceItem(funkin.ui.FullScreenScaleMode.gameNotchSize.x, 120 * (items.length - 1 + 1),
266282
defaultValue, available);
@@ -273,6 +289,8 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
273289

274290
preferenceItems.add(checkbox);
275291
preferenceDesc.push(prefDesc);
292+
293+
return checkbox;
276294
}
277295

278296
/**
@@ -286,13 +304,16 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
286304
* @param precision Rounds decimals up to a `precision` amount of digits (ex: 4 -> 0.1234, 2 -> 0.12)
287305
*/
288306
function createPrefItemNumber(prefName:String, prefDesc:String, onChange:Float->Void, ?valueFormatter:Float->String, defaultValue:Float, min:Float,
289-
max:Float, step:Float = 0.1, precision:Int):Void
307+
max:Float, step:Float = 0.1, precision:Int):NumberPreferenceItem
290308
{
291309
var item = new NumberPreferenceItem(funkin.ui.FullScreenScaleMode.gameNotchSize.x, (120 * items.length) + 30, prefName, defaultValue, min, max, step,
292310
precision, onChange, valueFormatter);
311+
293312
items.addItem(prefName, item);
294313
preferenceItems.add(item.lefthandText);
295314
preferenceDesc.push(prefDesc);
315+
316+
return item;
296317
}
297318

298319
/**
@@ -302,19 +323,23 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
302323
* @param min Minimum value (default = 0)
303324
* @param max Maximum value (default = 100)
304325
*/
305-
function createPrefItemPercentage(prefName:String, prefDesc:String, onChange:Int->Void, defaultValue:Int, min:Int = 0, max:Int = 100):Void
326+
function createPrefItemPercentage(prefName:String, prefDesc:String, onChange:Int->Void, defaultValue:Int, min:Int = 0, max:Int = 100):NumberPreferenceItem
306327
{
307328
var newCallback = function(value:Float) {
308329
onChange(Std.int(value));
309330
};
310331
var formatter = function(value:Float) {
311332
return '${value}%';
312333
};
334+
313335
var item = new NumberPreferenceItem(funkin.ui.FullScreenScaleMode.gameNotchSize.x, (120 * items.length) + 30, prefName, defaultValue, min, max, 10, 0,
314336
newCallback, formatter);
337+
315338
items.addItem(prefName, item);
316339
preferenceItems.add(item.lefthandText);
317340
preferenceDesc.push(prefDesc);
341+
342+
return item;
318343
}
319344

320345
/**
@@ -323,18 +348,44 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
323348
* @param onChange Gets called every time the player changes the value; use this to apply the value
324349
* @param defaultValue The value that is loaded in when the pref item is created (usually your Preferences.settingVariable)
325350
*/
326-
function createPrefItemEnum<T>(prefName:String, prefDesc:String, values:Map<String, T>, onChange:String->T->Void, defaultKey:String):Void
351+
function createPrefItemEnum<T>(prefName:String, prefDesc:String, values:Map<String, T>, onChange:String->T->Void, defaultKey:String):EnumPreferenceItem<T>
327352
{
328353
var item = new EnumPreferenceItem<T>(funkin.ui.FullScreenScaleMode.gameNotchSize.x, (120 * items.length) + 30, prefName, values, defaultKey, onChange);
354+
329355
items.addItem(prefName, item);
330356
preferenceItems.add(item.lefthandText);
331357
preferenceDesc.push(prefDesc);
358+
359+
return item;
360+
}
361+
362+
#if desktop
363+
function generateAudioDeviceEnums():Map<String, String>
364+
{
365+
var enums = ['Default' => 'Default'];
366+
var devices = AudioManager.getPlaybackDeviceNames();
367+
368+
for (device in devices)
369+
{
370+
enums.set(device, device);
371+
}
372+
373+
return enums;
374+
}
375+
376+
function refreshAudioDeviceItem(deviceName:String):Void
377+
{
378+
audioDeviceItem.changeEnums(generateAudioDeviceEnums(), Preferences.audioDevice);
332379
}
380+
#end
333381

334382
override function exit():Void
335383
{
336384
camFollow.setPosition(640, 30);
337385
menuCamera.snapToTarget();
386+
AudioManager.onDefaultPlaybackDeviceChanged.remove(refreshAudioDeviceItem);
387+
AudioManager.onPlaybackDeviceAdded.remove(refreshAudioDeviceItem);
388+
AudioManager.onPlaybackDeviceRemoved.remove(refreshAudioDeviceItem);
338389
super.exit();
339390
}
340391
}

source/funkin/ui/options/items/EnumPreferenceItem.hx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,23 @@ class EnumPreferenceItem<T> extends TextMenuItem
3030
{
3131
super(x, y, name, function() {
3232
var value = map.get(this.currentKey);
33-
callback(this.currentKey, value);
33+
this.onChangeCallback(this.currentKey, value);
3434
});
3535

3636
updateHitbox();
3737

38-
this.map = map;
39-
this.currentKey = defaultKey;
4038
this.onChangeCallback = callback;
39+
changeEnums(map, defaultKey);
40+
41+
lefthandText = new AtlasText(x + 15, y, formatted(defaultKey), AtlasFont.DEFAULT);
42+
43+
this.fireInstantly = true;
44+
}
45+
46+
public function changeEnums(map:Map<String, T>, ?defaultKey:String):Void
47+
{
48+
this.map = map;
49+
if (defaultKey != null) this.currentKey = defaultKey;
4150

4251
var i:Int = 0;
4352
for (key in map.keys())
@@ -48,10 +57,6 @@ class EnumPreferenceItem<T> extends TextMenuItem
4857
if (this.currentKey == key) index = i;
4958
i += 1;
5059
}
51-
52-
lefthandText = new AtlasText(x + 15, y, formatted(defaultKey), AtlasFont.DEFAULT);
53-
54-
this.fireInstantly = true;
5560
}
5661

5762
override function update(elapsed:Float):Void

0 commit comments

Comments
 (0)