Skip to content

Commit ed16a4b

Browse files
implementation of audio device switching
this shits kinda janky, buffers arent kept. maybe i should wait for openfl to add support for openal soft's device changing stuff but for now this will be right here
1 parent 3ca4b2f commit ed16a4b

File tree

2 files changed

+189
-2
lines changed

2 files changed

+189
-2
lines changed

source/Main.hx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import openfl.events.Event;
1818
import openfl.Lib;
1919
import openfl.media.Video;
2020
import openfl.net.NetStream;
21+
import funkin.audio.AudioSwitchFix;
2122

2223
// Adds support for FeralGamemode on Linux
2324
#if (linux && !DISABLE_GAMEMODE)
@@ -46,13 +47,17 @@ class Main extends Sprite
4647
#else
4748
var framerate:Int = 144; // How many frames per second the game should run at.
4849
#end
49-
*/
50-
var skipSplash:Bool = true; // Whether to skip the flixel splash screen that appears in release mode.
50+
*/ / var skipSplash:Bool = true; // Whether to skip the flixel splash screen that appears in release mode.
5151
var startFullscreen:Bool = false; // Whether to start the game in fullscreen on desktop targets
5252

5353
// You can pretty much ignore everything from here on - your code should go in your states.
5454
public static var lightMode:Bool = Sys.args().contains("-lightui");
5555

56+
// You can pretty much ignore everything from here on - your code should go in your states.
57+
// [ * -- INTERNAL VARIABLES - PLS DONT TOUCH THEM! -- * ] //
58+
@:dox(hide)
59+
public static var audioDisconnected:Bool = false; // Used for checking for audio device errors.
60+
5661
public static function main():Void
5762
{
5863
// Set the current working directory for Android and iOS devices
@@ -100,6 +105,8 @@ class Main extends Sprite
100105
// TODO: Replace with loadEnabledMods() once the user can configure the mod list.
101106
funkin.modding.PolymodHandler.loadAllMods();
102107

108+
AudioSwitchFix.init();
109+
103110
stage != null ? init() : addEventListener(Event.ADDED_TO_STAGE, init);
104111
}
105112

@@ -200,4 +207,12 @@ class Main extends Sprite
200207

201208
if (fpsCounter != null) fpsCounter.scaleX = fpsCounter.scaleY = (scale > 1 ? scale : 1);
202209
}
210+
211+
#if windows
212+
private override function __update(transformOnly:Bool, updateChildren:Bool):Void
213+
{
214+
super.__update(transformOnly, updateChildren);
215+
if (Main.audioDisconnected) AudioSwitchFix.reloadAudioDevice();
216+
}
217+
#end
203218
}

source/funkin/audio/AudioSwitchFix.hx

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
package funkin.audio;
2+
3+
import lime.media.AudioManager;
4+
import flixel.sound.FlxSound;
5+
import flixel.FlxState;
6+
7+
/**
8+
* if youre stealing this keep this comment at least please lol
9+
* hi gray itsa me yoshicrafter29 i fixed it hehe
10+
*
11+
*
12+
*
13+
* ok --- @charlescatyt
14+
* @see https://github.com/FNF-CNE-Devs/CodenameEngine/blob/main/source/funkin/backend/system/modules/AudioSwitchFix.hx
15+
*/
16+
#if windows
17+
@:buildXml('
18+
<target id="haxe">
19+
<lib name="dwmapi.lib" if="windows" />
20+
<lib name="shell32.lib" if="windows" />
21+
<lib name="gdi32.lib" if="windows" />
22+
<lib name="ole32.lib" if="windows" />
23+
<lib name="uxtheme.lib" if="windows" />
24+
</target>
25+
')
26+
// majority is taken from microsofts doc
27+
@:cppFileCode('
28+
#include "mmdeviceapi.h"
29+
#include "combaseapi.h"
30+
#include <iostream>
31+
#include <Windows.h>
32+
#include <cstdio>
33+
#include <tchar.h>
34+
#include <dwmapi.h>
35+
#include <winuser.h>
36+
#include <Shlobj.h>
37+
#include <wingdi.h>
38+
#include <shellapi.h>
39+
#define SAFE_RELEASE(punk) \\
40+
if ((punk) != NULL) \\
41+
{ (punk)->Release(); (punk) = NULL; }
42+
static long lastDefId = 0;
43+
class AudioFixClient : public IMMNotificationClient {
44+
LONG _cRef;
45+
IMMDeviceEnumerator *_pEnumerator;
46+
public:
47+
AudioFixClient() :
48+
_cRef(1),
49+
_pEnumerator(NULL)
50+
{
51+
HRESULT result = CoCreateInstance(__uuidof(MMDeviceEnumerator),
52+
NULL, CLSCTX_INPROC_SERVER,
53+
__uuidof(IMMDeviceEnumerator),
54+
(void**)&_pEnumerator);
55+
if (result == S_OK) {
56+
_pEnumerator->RegisterEndpointNotificationCallback(this);
57+
}
58+
}
59+
~AudioFixClient()
60+
{
61+
SAFE_RELEASE(_pEnumerator);
62+
}
63+
ULONG STDMETHODCALLTYPE AddRef()
64+
{
65+
return InterlockedIncrement(&_cRef);
66+
}
67+
ULONG STDMETHODCALLTYPE Release()
68+
{
69+
ULONG ulRef = InterlockedDecrement(&_cRef);
70+
if (0 == ulRef)
71+
{
72+
delete this;
73+
}
74+
return ulRef;
75+
}
76+
HRESULT STDMETHODCALLTYPE QueryInterface(
77+
REFIID riid, VOID **ppvInterface)
78+
{
79+
return S_OK;
80+
}
81+
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
82+
{
83+
::Main_obj::audioDisconnected = true;
84+
return S_OK;
85+
};
86+
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
87+
{
88+
return S_OK;
89+
}
90+
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
91+
LPCWSTR pwstrDeviceId,
92+
DWORD dwNewState)
93+
{
94+
return S_OK;
95+
}
96+
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
97+
LPCWSTR pwstrDeviceId,
98+
const PROPERTYKEY key)
99+
{
100+
return S_OK;
101+
}
102+
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
103+
EDataFlow flow, ERole role,
104+
LPCWSTR pwstrDeviceId)
105+
{
106+
::Main_obj::audioDisconnected = true;
107+
return S_OK;
108+
};
109+
};
110+
AudioFixClient *curAudioFix;
111+
')
112+
#end
113+
@:dox(hide)
114+
class AudioSwitchFix
115+
{
116+
// Reload audio device and replay all audio
117+
public static function reloadAudioDevice():Void
118+
{
119+
#if windows
120+
var playingList:Array<PlayingSound> = [];
121+
for (e in FlxG.sound.list)
122+
{
123+
if (e.playing)
124+
{
125+
playingList.push(
126+
{
127+
sound: e,
128+
time: e.time
129+
});
130+
e.stop();
131+
}
132+
}
133+
if (FlxG.sound.music != null) FlxG.sound.music.stop();
134+
AudioManager.suspend();
135+
#if !lime_doc_gen
136+
if (AudioManager.context.type == OPENAL)
137+
{
138+
var alc = AudioManager.context.openal;
139+
var buffer = alc.BUFFER;
140+
var buffersProcessed = alc.BUFFERS_PROCESSED;
141+
var buffersQueued = alc.BUFFERS_QUEUED;
142+
var byteOffset = alc.BYTE_OFFSET;
143+
var device = alc.openDevice();
144+
var ctx = alc.createContext(device);
145+
alc.makeContextCurrent(ctx);
146+
alc.processContext(ctx);
147+
alc.BUFFER = buffer;
148+
alc.BUFFERS_PROCESSED = buffersProcessed;
149+
alc.BUFFERS_QUEUED = buffersQueued;
150+
alc.BYTE_OFFSET = byteOffset;
151+
}
152+
#end
153+
AudioManager.resume();
154+
Main.audioDisconnected = false;
155+
for (e in playingList)
156+
{
157+
e.sound.play(true, e.time);
158+
}
159+
#end
160+
}
161+
162+
// Initialize the listener for audio device changes
163+
#if windows @:functionCode('if (!curAudioFix) curAudioFix = new AudioFixClient();') #end
164+
public static function init():Void {}
165+
}
166+
167+
// Helper for storing sound while fixing audio
168+
typedef PlayingSound =
169+
{
170+
var sound:FlxSound;
171+
var time:Float;
172+
}

0 commit comments

Comments
 (0)