Skip to content

Commit 7a9dfaa

Browse files
committed
Clean-up AudioManager, implement resume, shutdown, suspend for Web
1 parent defeaae commit 7a9dfaa

1 file changed

Lines changed: 190 additions & 87 deletions

File tree

src/lime/media/AudioManager.hx

Lines changed: 190 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,163 +1,266 @@
11
package lime.media;
22

3-
import lime.system.CFFIPointer;
43
import haxe.MainLoop;
5-
import haxe.Timer;
6-
import lime._internal.backend.native.NativeCFFI;
4+
import lime.app.Event;
5+
import lime.system.CFFIPointer;
6+
import lime.system.System;
7+
#if lime_openal
78
import lime.media.openal.AL;
89
import lime.media.openal.ALC;
910
import lime.media.openal.ALContext;
1011
import lime.media.openal.ALDevice;
11-
import lime.app.Application;
12-
#if (js && html5)
13-
import js.Browser;
12+
#elseif lime_howlerjs
13+
import lime.media.howlerjs.Howler;
1414
#end
1515

1616
#if !lime_debug
1717
@:fileXml('tags="haxe,release"')
1818
@:noDebug
1919
#end
20-
@:access(lime._internal.backend.native.NativeCFFI)
2120
@:access(lime.media.openal.ALDevice)
2221
class AudioManager
2322
{
23+
/**
24+
The current used context to use for the audio manager.
25+
**/
2426
public static var context:AudioContext;
2527

28+
#if lime_openal
29+
@:noCompletion private static var __disconnectExtSupported:Bool;
30+
@:noCompletion private static var __reopenDeviceSupported:Bool;
31+
@:noCompletion private static var __systemEventsSupported:Bool;
32+
#end
33+
34+
/**
35+
Initializes an `AudioManager` to playback to and capture from audio devices.
36+
Automatically dispatched when Application is constructed.
37+
38+
@param context Optional; An Audio Context to initalize the `AudioManager` with.
39+
**/
2640
public static function init(context:AudioContext = null)
2741
{
28-
if (AudioManager.context == null)
42+
if (AudioManager.context != null) return;
43+
44+
if (context == null)
2945
{
30-
if (context == null)
31-
{
32-
AudioManager.context = new AudioContext();
46+
context = new AudioContext();
47+
}
3348

34-
context = AudioManager.context;
49+
AudioManager.context = context;
3550

36-
#if !lime_doc_gen
37-
if (context.type == OPENAL)
38-
{
39-
var alc = context.openal;
40-
var device = alc.openDevice();
41-
var ctx = alc.createContext(device);
51+
#if lime_openal
52+
if (context.type == OPENAL)
53+
{
54+
refresh();
4255

43-
alc.makeContextCurrent(ctx);
44-
alc.processContext(ctx);
56+
#if !(neko || mobile)
57+
if (__reopenDeviceSupported) AL.disable(AL.STOP_SOURCES_ON_DISCONNECT_SOFT);
58+
if (__systemEventsSupported) {
59+
ALC.eventControlSOFT([
60+
ALC.EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT,
61+
ALC.EVENT_TYPE_DEVICE_ADDED_SOFT,
62+
ALC.EVENT_TYPE_DEVICE_REMOVED_SOFT],
63+
true);
64+
ALC.eventCallbackSOFT(__deviceEventCallback);
65+
}
66+
#end
67+
}
68+
#end
69+
}
4570

46-
#if !(neko || mobile)
47-
if (alc.isExtensionPresent('ALC_SOFT_system_events', device) && alc.isExtensionPresent('ALC_SOFT_reopen_device', device))
48-
{
49-
alc.disable(AL.STOP_SOURCES_ON_DISCONNECT_SOFT);
71+
/**
72+
Refresh the context with optionally to different device.
5073
51-
alc.eventControlSOFT([ALC.EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT, ALC.EVENT_TYPE_DEVICE_ADDED_SOFT, ALC.EVENT_TYPE_DEVICE_REMOVED_SOFT], true);
74+
Only works on Native target.
5275
53-
alc.eventCallbackSOFT(deviceEventCallback);
54-
}
55-
#end
56-
}
57-
#end
58-
}
76+
@param deviceName Optional; The device name to use to for playbacking the audios.
77+
**/
78+
public static function refresh(?deviceName:String):Bool
79+
{
80+
#if (lime_openal && !lime_doc_gen)
81+
if (context == null || context.type != OPENAL) return false;
5982

60-
AudioManager.context = context;
83+
var currentContext = ALC.getCurrentContext();
84+
var device = currentContext != null ? ALC.getContextsDevice(currentContext) : null;
85+
86+
if (device != null && __reopenDeviceSupported && ALC.reopenDeviceSOFT(device, deviceName, null))
87+
{
88+
__refresh();
89+
return true;
90+
}
91+
92+
if (currentContext != null)
93+
{
94+
ALC.destroyContext(currentContext);
95+
currentContext = null;
96+
}
97+
98+
if (device != null)
99+
{
100+
ALC.closeDevice(device);
101+
device = null;
61102
}
103+
104+
if ((device = ALC.openDevice()) == null || (currentContext = ALC.createContext(device)) == null
105+
|| !ALC.makeContextCurrent(currentContext))
106+
{
107+
return false;
108+
}
109+
110+
ALC.processContext(currentContext);
111+
__refresh();
112+
return true;
113+
#else
114+
return false;
115+
#end
62116
}
63117

118+
/**
119+
Resumes the current `AudioManager` context.
120+
121+
This function does not work on the Flash target.
122+
**/
64123
public static function resume():Void
65124
{
66-
#if !lime_doc_gen
125+
#if (lime_openal && !lime_doc_gen)
67126
if (context != null && context.type == OPENAL)
68127
{
69-
var alc = context.openal;
70-
var currentContext = alc.getCurrentContext();
71-
128+
var currentContext = ALC.getCurrentContext();
72129
if (currentContext != null)
73130
{
74-
var device = alc.getContextsDevice(currentContext);
75-
alc.resumeDevice(device);
76-
alc.processContext(currentContext);
131+
var device = ALC.getContextsDevice(currentContext);
132+
if (device != null) ALC.resumeDevice(device);
133+
134+
ALC.processContext(currentContext);
77135
}
78136
}
137+
#elseif (js && html5)
138+
if (context != null && context.type == WEB)
139+
{
140+
context.web.resume();
141+
}
79142
#end
80143
}
81144

145+
/**
146+
Shutdowns the current `AudioManager` context.
147+
148+
This function does not work on the Flash target.
149+
**/
82150
public static function shutdown():Void
83151
{
84-
#if !lime_doc_gen
152+
#if (lime_openal && !lime_doc_gen)
85153
if (context != null && context.type == OPENAL)
86154
{
87-
var alc = context.openal;
88-
var currentContext = alc.getCurrentContext();
89-
var device = alc.getContextsDevice(currentContext);
90-
155+
var currentContext = ALC.getCurrentContext();
91156
if (currentContext != null)
92157
{
93-
alc.makeContextCurrent(null);
94-
alc.destroyContext(currentContext);
158+
ALC.makeContextCurrent(null);
159+
ALC.destroyContext(currentContext);
95160

96-
if (device != null)
97-
{
98-
alc.closeDevice(device);
99-
}
161+
var device = ALC.getContextsDevice(currentContext);
162+
if (device != null) ALC.closeDevice(device);
100163
}
101164
}
165+
#elseif (js && html5)
166+
if (context != null && context.type == WEB)
167+
{
168+
#if lime_howlerjs
169+
Howler.unload();
170+
#else
171+
context.web.close();
172+
#end
173+
}
102174
#end
103175

104176
context = null;
105177
}
106178

179+
/**
180+
Pauses the current `AudioManager` context.
181+
182+
This function does not work on the Flash target.
183+
**/
107184
public static function suspend():Void
108185
{
109-
#if !lime_doc_gen
186+
#if (lime_openal && !lime_doc_gen)
110187
if (context != null && context.type == OPENAL)
111188
{
112-
var alc = context.openal;
113-
var currentContext = alc.getCurrentContext();
114-
var device = alc.getContextsDevice(currentContext);
115-
189+
var currentContext = ALC.getCurrentContext();
116190
if (currentContext != null)
117191
{
118-
alc.suspendContext(currentContext);
192+
ALC.suspendContext(currentContext);
119193

120-
if (device != null)
121-
{
122-
alc.pauseDevice(device);
123-
}
194+
var device = ALC.getContextsDevice(currentContext);
195+
if (device != null) ALC.pauseDevice(device);
124196
}
125197
}
198+
#elseif (js && html5)
199+
if (context != null && context.type == WEB)
200+
{
201+
context.web.suspend();
202+
}
126203
#end
127204
}
128205

129-
@:noCompletion
130-
#if hl
131-
private static function deviceEventCallback(eventType:Int, deviceType:Int, handle:CFFIPointer, message:hl.Bytes):Void
132-
#else
133-
private static function deviceEventCallback(eventType:Int, deviceType:Int, handle:CFFIPointer, message:String):Void
134-
#end
206+
#if (lime_openal && !lime_doc_gen)
207+
// device is null... and its actually intended cuz its tied to the device its being used rn.
208+
// why
209+
// and in ALC.EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT, deviceName is the device GUID
210+
#if !(neko || mobile)
211+
@:noCompletion private static function __deviceEventCallback(eventType:Int, deviceType:Int, handle:CFFIPointer,
212+
#if hl _message:hl.Bytes #else message:String #end)
135213
{
136-
#if !lime_doc_gen
137-
if (eventType == ALC.EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT && deviceType == ALC.PLAYBACK_DEVICE_SOFT)
138-
{
139-
var device = new ALDevice(handle);
214+
#if hl var message:String = CFFI.stringValue(_message); #end
215+
var device:ALDevice = handle != null ? new ALDevice(handle) : null;
216+
var deviceName = __getDeviceNameFromMessage(message);
140217

141-
MainLoop.runInMainThread(function():Void
142-
{
143-
var alc = context.openal;
218+
var currentContext = ALC.getCurrentContext();
219+
var currentDevice = currentContext != null ? ALC.getContextsDevice(currentContext) : null;
220+
if (deviceType == ALC.PLAYBACK_DEVICE_SOFT) {
221+
switch (eventType) {
222+
case ALC.EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT:
223+
refresh();
224+
case ALC.EVENT_TYPE_DEVICE_ADDED_SOFT:
144225

145-
if (device == null)
146-
{
147-
var currentContext = alc.getCurrentContext();
226+
case ALC.EVENT_TYPE_DEVICE_REMOVED_SOFT:
227+
if (__disconnectExtSupported && ALC.getIntegerv(currentDevice, ALC.CONNECTED, 1)[0] != 1) refresh();
228+
}
229+
}
230+
else if (deviceType == ALC.CAPTURE_DEVICE_SOFT) {
231+
switch (eventType) {
232+
case ALC.EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT:
148233

149-
var device = alc.getContextsDevice(currentContext);
234+
case ALC.EVENT_TYPE_DEVICE_ADDED_SOFT:
150235

151-
if (device != null)
152-
alc.reopenDeviceSOFT(device, null, null);
153-
}
154-
else
155-
{
156-
alc.reopenDeviceSOFT(device, null, null);
157-
}
236+
case ALC.EVENT_TYPE_DEVICE_REMOVED_SOFT:
158237

159-
});
238+
}
160239
}
161-
#end
162240
}
163-
}
241+
#end
242+
243+
@:noCompletion private static function __refresh():Void
244+
{
245+
if (context == null || context.type != OPENAL) return;
246+
247+
var currentContext = ALC.getCurrentContext();
248+
if (currentContext == null) return;
249+
250+
var device = ALC.getContextsDevice(currentContext);
251+
if (device == null) return;
252+
253+
__disconnectExtSupported = ALC.isExtensionPresent(null, 'ALC_EXT_disconnect');
254+
__reopenDeviceSupported = ALC.isExtensionPresent(null, 'ALC_SOFT_reopen_device');
255+
__systemEventsSupported = ALC.isExtensionPresent(null, 'ALC_SOFT_system_events');
256+
}
257+
258+
// permanent band-aid fix whatever
259+
@:noCompletion private static function __getDeviceNameFromMessage(message:String):Null<String>
260+
{
261+
if (StringTools.startsWith(message, 'Device removed: ')) return message.substr(16);
262+
else if (StringTools.startsWith(message, 'Device added: ')) return message.substr(14);
263+
else return null;
264+
}
265+
#end
266+
}

0 commit comments

Comments
 (0)