Skip to content

Commit 2daae74

Browse files
committed
Add Audio Device Switching Option
1 parent c879239 commit 2daae74

File tree

4 files changed

+115
-11
lines changed

4 files changed

+115
-11
lines changed

source/funkin/Preferences.hx

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

418+
#if desktop
419+
/**
420+
* What audio device should it playback sounds to.
421+
* @default Default
422+
*/
423+
public static var audioDevice(get, set):String;
424+
425+
static function get_audioDevice():String
426+
{
427+
return Save?.instance?.options?.audioDevice ?? "Default";
428+
}
429+
430+
static function set_audioDevice(value:String):String
431+
{
432+
if (value == Save.instance.options.audioDevice) return value;
433+
434+
var save:Save = Save.instance;
435+
FlxG.sound.automaticDefaultDevice = value == "Default" || (FlxG.sound.deviceName = value) != value;
436+
437+
save.options.audioDevice = FlxG.sound.automaticDefaultDevice ? "Default" : value;
438+
Save.system.flush();
439+
440+
return value;
441+
}
442+
#end
443+
418444
/**
419445
* If enabled, the game will hide the mouse when taking a screenshot.
420446
* @default `true`
@@ -484,6 +510,20 @@ class Preferences
484510
setDebugDisplayMode(Preferences.debugDisplay);
485511
setDebugDisplayBGOpacity(Preferences.debugDisplayBGOpacity / 100);
486512

513+
#if desktop
514+
// Apply audio device preference, if failed, fallback to Default.
515+
FlxG.sound.deviceName = Preferences.audioDevice;
516+
if (FlxG.sound.deviceName == Preferences.audioDevice)
517+
{
518+
FlxG.sound.automaticDefaultDevice = false;
519+
}
520+
else
521+
{
522+
Preferences.audioDevice = "Default";
523+
FlxG.sound.automaticDefaultDevice = true;
524+
}
525+
#end
526+
487527
#if web
488528
toggleFramerateCap(Preferences.unlockedFramerate);
489529
#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,
@@ -1236,6 +1237,12 @@ typedef SaveDataOptions =
12361237
*/
12371238
var unlockedFramerate:Bool;
12381239

1240+
/**
1241+
* What audio device should it playback sounds to.
1242+
* @default 'Default'
1243+
*/
1244+
var audioDevice:String;
1245+
12391246
/**
12401247
* Screenshot options
12411248
* @param shouldHideMouse Should the mouse be hidden when taking a screenshot? Default: `true`

source/funkin/ui/options/PreferencesMenu.hx

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
3939
var hudCamera:FlxCamera;
4040
var camFollow:FlxObject;
4141

42+
#if desktop
43+
var audioDeviceItem:EnumPreferenceItem<String>;
44+
#end
45+
4246
public function new()
4347
{
4448
super();
@@ -170,6 +174,17 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
170174
}, Preferences.autoFullscreen);
171175
#end
172176

177+
#if desktop
178+
audioDeviceItem = createPrefItemEnum('Audio Device', 'What audio device should the game playbacks sounds to.', generateAudioDeviceEnums(),
179+
(key:String, value:String) ->
180+
{
181+
Preferences.audioDevice = value;
182+
}, Preferences.audioDevice);
183+
FlxG.sound.onDefaultDeviceChanged.add(refreshAudioDeviceItem);
184+
FlxG.sound.onDeviceAdded.add(refreshAudioDeviceItem);
185+
FlxG.sound.onDeviceRemoved.add(refreshAudioDeviceItem);
186+
#end
187+
173188
#if web
174189
createPrefItemCheckbox('Unlocked Framerate', 'When enabled, the framerate is unlocked.', function(value:Bool):Void {
175190
Preferences.unlockedFramerate = value;
@@ -260,7 +275,7 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
260275
* @param onChange Gets called every time the player changes the value; use this to apply the value
261276
* @param defaultValue The value that is loaded in when the pref item is created (usually your Preferences.settingVariable)
262277
*/
263-
function createPrefItemCheckbox(prefName:String, prefDesc:String, onChange:Bool->Void, defaultValue:Bool, available:Bool = true):Void
278+
function createPrefItemCheckbox(prefName:String, prefDesc:String, onChange:Bool->Void, defaultValue:Bool, available:Bool = true):CheckboxPreferenceItem
264279
{
265280
var checkbox:CheckboxPreferenceItem = new CheckboxPreferenceItem(funkin.ui.FullScreenScaleMode.gameNotchSize.x, 120 * (items.length - 1 + 1),
266281
defaultValue, available);
@@ -273,6 +288,8 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
273288

274289
preferenceItems.add(checkbox);
275290
preferenceDesc.push(prefDesc);
291+
292+
return checkbox;
276293
}
277294

278295
/**
@@ -286,13 +303,16 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
286303
* @param precision Rounds decimals up to a `precision` amount of digits (ex: 4 -> 0.1234, 2 -> 0.12)
287304
*/
288305
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
306+
max:Float, step:Float = 0.1, precision:Int):NumberPreferenceItem
290307
{
291308
var item = new NumberPreferenceItem(funkin.ui.FullScreenScaleMode.gameNotchSize.x, (120 * items.length) + 30, prefName, defaultValue, min, max, step,
292309
precision, onChange, valueFormatter);
310+
293311
items.addItem(prefName, item);
294312
preferenceItems.add(item.lefthandText);
295313
preferenceDesc.push(prefDesc);
314+
315+
return item;
296316
}
297317

298318
/**
@@ -302,19 +322,23 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
302322
* @param min Minimum value (default = 0)
303323
* @param max Maximum value (default = 100)
304324
*/
305-
function createPrefItemPercentage(prefName:String, prefDesc:String, onChange:Int->Void, defaultValue:Int, min:Int = 0, max:Int = 100):Void
325+
function createPrefItemPercentage(prefName:String, prefDesc:String, onChange:Int->Void, defaultValue:Int, min:Int = 0, max:Int = 100):NumberPreferenceItem
306326
{
307327
var newCallback = function(value:Float) {
308328
onChange(Std.int(value));
309329
};
310330
var formatter = function(value:Float) {
311331
return '${value}%';
312332
};
333+
313334
var item = new NumberPreferenceItem(funkin.ui.FullScreenScaleMode.gameNotchSize.x, (120 * items.length) + 30, prefName, defaultValue, min, max, 10, 0,
314335
newCallback, formatter);
336+
315337
items.addItem(prefName, item);
316338
preferenceItems.add(item.lefthandText);
317339
preferenceDesc.push(prefDesc);
340+
341+
return item;
318342
}
319343

320344
/**
@@ -323,18 +347,46 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
323347
* @param onChange Gets called every time the player changes the value; use this to apply the value
324348
* @param defaultValue The value that is loaded in when the pref item is created (usually your Preferences.settingVariable)
325349
*/
326-
function createPrefItemEnum<T>(prefName:String, prefDesc:String, values:Map<String, T>, onChange:String->T->Void, defaultKey:String):Void
350+
function createPrefItemEnum<T>(prefName:String, prefDesc:String, values:Map<String, T>, onChange:String->T->Void, defaultKey:String):EnumPreferenceItem<T>
327351
{
328352
var item = new EnumPreferenceItem<T>(funkin.ui.FullScreenScaleMode.gameNotchSize.x, (120 * items.length) + 30, prefName, values, defaultKey, onChange);
353+
329354
items.addItem(prefName, item);
330355
preferenceItems.add(item.lefthandText);
331356
preferenceDesc.push(prefDesc);
357+
358+
return item;
359+
}
360+
361+
#if desktop
362+
function generateAudioDeviceEnums():Map<String, String>
363+
{
364+
var enums = ['Default' => 'Default'];
365+
var devices = lime.media.AudioManager.getPlaybackDeviceNames();
366+
367+
for (device in devices)
368+
{
369+
enums.set(device, device);
370+
}
371+
372+
return enums;
332373
}
333374

375+
function refreshAudioDeviceItem(deviceName:String):Void
376+
{
377+
audioDeviceItem.changeEnums(generateAudioDeviceEnums(), Preferences.audioDevice);
378+
}
379+
#end
380+
334381
override function exit():Void
335382
{
336383
camFollow.setPosition(640, 30);
337384
menuCamera.snapToTarget();
385+
#if desktop
386+
FlxG.sound.onDefaultDeviceChanged.remove(refreshAudioDeviceItem);
387+
FlxG.sound.onDeviceAdded.remove(refreshAudioDeviceItem);
388+
FlxG.sound.onDeviceRemoved.remove(refreshAudioDeviceItem);
389+
#end
338390
super.exit();
339391
}
340392
}

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)