Skip to content

Commit

Permalink
implementation of audio device switching
Browse files Browse the repository at this point in the history
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
  • Loading branch information
charlesisfeline committed Oct 16, 2024
1 parent 3ca4b2f commit ed16a4b
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 2 deletions.
19 changes: 17 additions & 2 deletions source/Main.hx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import openfl.events.Event;
import openfl.Lib;
import openfl.media.Video;
import openfl.net.NetStream;
import funkin.audio.AudioSwitchFix;

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

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

// You can pretty much ignore everything from here on - your code should go in your states.
// [ * -- INTERNAL VARIABLES - PLS DONT TOUCH THEM! -- * ] //
@:dox(hide)
public static var audioDisconnected:Bool = false; // Used for checking for audio device errors.

public static function main():Void
{
// Set the current working directory for Android and iOS devices
Expand Down Expand Up @@ -100,6 +105,8 @@ class Main extends Sprite
// TODO: Replace with loadEnabledMods() once the user can configure the mod list.
funkin.modding.PolymodHandler.loadAllMods();

AudioSwitchFix.init();

stage != null ? init() : addEventListener(Event.ADDED_TO_STAGE, init);
}

Expand Down Expand Up @@ -200,4 +207,12 @@ class Main extends Sprite

if (fpsCounter != null) fpsCounter.scaleX = fpsCounter.scaleY = (scale > 1 ? scale : 1);
}

#if windows
private override function __update(transformOnly:Bool, updateChildren:Bool):Void
{
super.__update(transformOnly, updateChildren);
if (Main.audioDisconnected) AudioSwitchFix.reloadAudioDevice();
}
#end
}
172 changes: 172 additions & 0 deletions source/funkin/audio/AudioSwitchFix.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package funkin.audio;

import lime.media.AudioManager;
import flixel.sound.FlxSound;
import flixel.FlxState;

/**
* if youre stealing this keep this comment at least please lol
* hi gray itsa me yoshicrafter29 i fixed it hehe
*
*
*
* ok --- @charlescatyt
* @see https://github.com/FNF-CNE-Devs/CodenameEngine/blob/main/source/funkin/backend/system/modules/AudioSwitchFix.hx
*/
#if windows
@:buildXml('
<target id="haxe">
<lib name="dwmapi.lib" if="windows" />
<lib name="shell32.lib" if="windows" />
<lib name="gdi32.lib" if="windows" />
<lib name="ole32.lib" if="windows" />
<lib name="uxtheme.lib" if="windows" />
</target>
')
// majority is taken from microsofts doc
@:cppFileCode('
#include "mmdeviceapi.h"
#include "combaseapi.h"
#include <iostream>
#include <Windows.h>
#include <cstdio>
#include <tchar.h>
#include <dwmapi.h>
#include <winuser.h>
#include <Shlobj.h>
#include <wingdi.h>
#include <shellapi.h>
#define SAFE_RELEASE(punk) \\
if ((punk) != NULL) \\
{ (punk)->Release(); (punk) = NULL; }
static long lastDefId = 0;
class AudioFixClient : public IMMNotificationClient {
LONG _cRef;
IMMDeviceEnumerator *_pEnumerator;
public:
AudioFixClient() :
_cRef(1),
_pEnumerator(NULL)
{
HRESULT result = CoCreateInstance(__uuidof(MMDeviceEnumerator),
NULL, CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator),
(void**)&_pEnumerator);
if (result == S_OK) {
_pEnumerator->RegisterEndpointNotificationCallback(this);
}
}
~AudioFixClient()
{
SAFE_RELEASE(_pEnumerator);
}
ULONG STDMETHODCALLTYPE AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG ulRef = InterlockedDecrement(&_cRef);
if (0 == ulRef)
{
delete this;
}
return ulRef;
}
HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid, VOID **ppvInterface)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
{
::Main_obj::audioDisconnected = true;
return S_OK;
};
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
LPCWSTR pwstrDeviceId,
DWORD dwNewState)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
LPCWSTR pwstrDeviceId,
const PROPERTYKEY key)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
EDataFlow flow, ERole role,
LPCWSTR pwstrDeviceId)
{
::Main_obj::audioDisconnected = true;
return S_OK;
};
};
AudioFixClient *curAudioFix;
')
#end
@:dox(hide)
class AudioSwitchFix
{
// Reload audio device and replay all audio
public static function reloadAudioDevice():Void
{
#if windows
var playingList:Array<PlayingSound> = [];
for (e in FlxG.sound.list)
{
if (e.playing)
{
playingList.push(
{
sound: e,
time: e.time
});
e.stop();
}
}
if (FlxG.sound.music != null) FlxG.sound.music.stop();
AudioManager.suspend();
#if !lime_doc_gen
if (AudioManager.context.type == OPENAL)
{
var alc = AudioManager.context.openal;
var buffer = alc.BUFFER;
var buffersProcessed = alc.BUFFERS_PROCESSED;
var buffersQueued = alc.BUFFERS_QUEUED;
var byteOffset = alc.BYTE_OFFSET;
var device = alc.openDevice();
var ctx = alc.createContext(device);
alc.makeContextCurrent(ctx);
alc.processContext(ctx);
alc.BUFFER = buffer;
alc.BUFFERS_PROCESSED = buffersProcessed;
alc.BUFFERS_QUEUED = buffersQueued;
alc.BYTE_OFFSET = byteOffset;
}
#end
AudioManager.resume();
Main.audioDisconnected = false;
for (e in playingList)
{
e.sound.play(true, e.time);
}
#end
}

// Initialize the listener for audio device changes
#if windows @:functionCode('if (!curAudioFix) curAudioFix = new AudioFixClient();') #end
public static function init():Void {}
}

// Helper for storing sound while fixing audio
typedef PlayingSound =
{
var sound:FlxSound;
var time:Float;
}

0 comments on commit ed16a4b

Please sign in to comment.