notifications_default_interface.pbi
Code: Select all
EnableExplicit
Enumeration #PB_Event_FirstCustomValue
#EventStateInactive
#EventStateActive
#EventStateExpired
EndEnumeration
Enumeration
#AudioSessionStateInactive
#AudioSessionStateActive
#AudioSessionStateExpired
EndEnumeration
#AUDCLNT_STREAMFLAGS_CROSSPROCESS = $00010000
#CLSCTX_ALL = #CLSCTX_INPROC_SERVER | #CLSCTX_INPROC_HANDLER | #CLSCTX_LOCAL_SERVER | #CLSCTX_REMOTE_SERVER
Interface IMMDeviceEnumerator Extends IUnknown
EnumAudioEndpoints(dataFlow, dwStateMask, ppDevices)
GetDefaultAudioEndpoint(dataFlow, role, ppEndpoint)
GetDevice(pwstrId, ppDevice)
RegisterEndpointNotificationCallback(pClient)
UnregisterEndpointNotificationCallback(pClient)
EndInterface
Interface IMMDevice Extends IUnknown
Activate(iid, dwClsCtx, pActivationParams, ppInterface)
OpenPropertyStore(stgmAccess, ppProperties)
GetId(ppstrId)
GetState(pdwState)
EndInterface
Interface IAudioSessionManager Extends IUnknown
GetAudioSessionControl(AudioSessionGuid, StreamFlags, SessionControl)
GetSimpleAudioVolume(AudioSessionGuid, StreamFlags, AudioVolume)
EndInterface
Interface IAudioSessionManager2 Extends IAudioSessionManager
GetSessionEnumerator(SessionEnum)
RegisterSessionNotification(Notification)
UnregisterSessionNotification(Notification)
RegisterDuckNotification(sessionID, duckNotification)
UnregisterDuckNotification(duckNotification)
EndInterface
Interface IAudioSessionNotification Extends IUnknown
OnSessionCreated(NewSession)
EndInterface
Interface IAudioSessionControl Extends IUnknown
GetState(pRetVal)
GetDisplayName(*pRetVal)
SetDisplayName(Value, EventContext)
GetIconPath(pRetVal)
SetIconPath(Value, EventContext)
GetGroupingParam(pRetVal)
SetGroupingParam(Override, EventContext)
RegisterAudioSessionNotification(NewNotifications)
UnregisterAudioSessionNotification(NewNotifications)
EndInterface
Interface IAudioSessionControl2 Extends IAudioSessionControl
GetSessionIdentifier(*pRetVal)
GetSessionInstanceIdentifier(*pRetVal)
GetProcessId(pRetVal)
IsSystemSoundsSession()
SetDuckingPreference(optOut)
EndInterface
Interface IAudioSessionEnumerator Extends IUnknown
GetCount(count)
GetSession(sessionCount, session)
EndInterface
Interface IAudioSessionEvents Extends IUnknown
OnDisplayNameChanged(NewDisplayName, EventContext)
OnIconPathChanged(NewIconPath, EventContext)
OnSimpleVolumeChanged(NewVolume, NewMute, EventContext)
OnChannelVolumeChanged(ChannelCount, NewChannelVolumeArray, ChangedChannel, EventContext)
OnGroupingParamChanged(NewGroupingParam, EventContext)
OnStateChanged(NewState)
OnSessionDisconnected(DisconnectReason)
EndInterface
Structure MyAudioSessionEventsObj
lpVtbl.i
RefCount.l
ProcessID.l
*SessionID
pSessionControl.IAudioSessionControl2
EndStructure
Structure MyAudioSessionNotificationObj
lpVtbl.i
RefCount.l
EndStructure
Structure mysessions
*object.MyAudioSessionEventsObj
EndStructure
Declare MyOnSessionCreated(*Object, pSessionControl.IAudioSessionControl2)
Procedure MyAddRef(*Object)
Protected newCount = PeekL(*Object + SizeOf(Integer)) + 1
PokeL(*Object + SizeOf(Integer), newCount)
ProcedureReturn newCount
EndProcedure
Procedure MyQueryInterface(*Object, riid.i, ppvObject.i)
PokeI(ppvObject, *Object)
MyAddRef(*Object)
ProcedureReturn #S_OK
EndProcedure
Procedure MyRelease(*Object)
Protected newCount = PeekL(*Object + SizeOf(Integer)) - 1
PokeL(*Object + SizeOf(Integer), newCount)
If newCount = 0
FreeStructure(*Object)
*Object = #Null
ProcedureReturn newCount
Else
ProcedureReturn newCount
EndIf
EndProcedure
Procedure CleanUp(*Object.MyAudioSessionEventsObj)
CoTaskMemFree_(*Object\SessionID)
FreeMemory(*Object\lpVtbl)
*Object\pSessionControl\Release()
MyRelease(*Object)
EndProcedure
Procedure MySessionEvents_OnSessionDisconnected(*Object.MyAudioSessionEventsObj, DisconnectReason)
Debug "Session Disconnected: PID=" + Str(*Object\ProcessID) + ", Reason=" + Str(DisconnectReason)
ProcedureReturn #S_OK
EndProcedure
Procedure MySessionEvents_OnDisplayNameChanged(*Object.MyAudioSessionEventsObj, NewDisplayName, EventContext)
ProcedureReturn #S_OK
EndProcedure
Procedure MySessionEvents_OnIconPathChanged(*Object.MyAudioSessionEventsObj, NewIconPath, EventContext)
ProcedureReturn #S_OK
EndProcedure
Procedure MySessionEvents_OnSimpleVolumeChanged(*Object.MyAudioSessionEventsObj, NewVolume, NewMute, EventContext)
ProcedureReturn #S_OK
EndProcedure
Procedure MySessionEvents_OnChannelVolumeChanged(*Object.MyAudioSessionEventsObj, ChannelCount, NewChannelVolumeArray, ChangedChannel, EventContext)
ProcedureReturn #S_OK
EndProcedure
Procedure MySessionEvents_OnGroupingParamChanged(*Object.MyAudioSessionEventsObj, NewGroupingParam, EventContext)
ProcedureReturn #S_OK
EndProcedure
Procedure MySessionEvents_OnStateChanged(*Object.MyAudioSessionEventsObj, NewState)
Select NewState
Case #AudioSessionStateInactive
PostEvent(#EventStateInactive, #PB_Ignore, #PB_Ignore, #PB_Ignore, *Object)
Case #AudioSessionStateActive
PostEvent(#EventStateActive, #PB_Ignore, #PB_Ignore, #PB_Ignore, *Object)
Case #AudioSessionStateExpired
PostEvent(#EventStateExpired, #PB_Ignore, #PB_Ignore, #PB_Ignore, *Object)
EndSelect
ProcedureReturn #S_OK
EndProcedure
Procedure CreateMyAudioSessionEvents(ProcessID, *SessionID, pSessionControl)
Protected *pObj.MyAudioSessionEventsObj = AllocateStructure(MyAudioSessionEventsObj)
Protected *MyAudioSessionEventsVTable = AllocateMemory(10 * SizeOf(Integer))
If *pObj = 0
ProcedureReturn #Null
EndIf
If *MyAudioSessionEventsVTable = #Null
FreeStructure(*pObj)
ProcedureReturn #Null
EndIf
PokeI(*MyAudioSessionEventsVTable + 0 * SizeOf(Integer), @MyQueryInterface())
PokeI(*MyAudioSessionEventsVTable + 1 * SizeOf(Integer), @MyAddRef())
PokeI(*MyAudioSessionEventsVTable + 2 * SizeOf(Integer), @MyRelease())
PokeI(*MyAudioSessionEventsVTable + 3 * SizeOf(Integer), @MySessionEvents_OnDisplayNameChanged())
PokeI(*MyAudioSessionEventsVTable + 4 * SizeOf(Integer), @MySessionEvents_OnIconPathChanged())
PokeI(*MyAudioSessionEventsVTable + 5 * SizeOf(Integer), @MySessionEvents_OnSimpleVolumeChanged())
PokeI(*MyAudioSessionEventsVTable + 6 * SizeOf(Integer), @MySessionEvents_OnChannelVolumeChanged())
PokeI(*MyAudioSessionEventsVTable + 7 * SizeOf(Integer), @MySessionEvents_OnGroupingParamChanged())
PokeI(*MyAudioSessionEventsVTable + 8 * SizeOf(Integer), @MySessionEvents_OnStateChanged())
PokeI(*MyAudioSessionEventsVTable + 9 * SizeOf(Integer), @MySessionEvents_OnSessionDisconnected())
*pObj\lpVtbl = *MyAudioSessionEventsVTable
*pObj\ProcessID = ProcessID
*pObj\pSessionControl = pSessionControl
*pObj\SessionID = *SessionID
*pObj\RefCount = 1
ProcedureReturn *pObj
EndProcedure
Procedure CreateMyAudioSessionNotification()
Protected *pObj.MyAudioSessionNotificationObj = AllocateStructure(MyAudioSessionNotificationObj)
Protected *MyAudioSessionNotificationVTable = AllocateMemory(4 * SizeOf(Integer))
If *pObj = 0
ProcedureReturn #Null
EndIf
If *MyAudioSessionNotificationVTable = #Null
FreeStructure(*pObj)
ProcedureReturn #Null
EndIf
PokeI(*MyAudioSessionNotificationVTable + 0 * SizeOf(Integer), @MyQueryInterface())
PokeI(*MyAudioSessionNotificationVTable + 1 * SizeOf(Integer), @MyAddRef())
PokeI(*MyAudioSessionNotificationVTable + 2 * SizeOf(Integer), @MyRelease())
PokeI(*MyAudioSessionNotificationVTable + 3 * SizeOf(Integer), @MyOnSessionCreated())
*pObj\lpVtbl = *MyAudioSessionNotificationVTable
*pObj\RefCount = 1
ProcedureReturn *pObj
EndProcedure
Procedure MyOnSessionCreated(*Object.MyAudioSessionNotificationObj, pSessionControl.IAudioSessionControl2)
Protected pSessionId, PID, sessionEvents, fname.s, state
pSessionControl\AddRef()
pSessionControl\GetProcessId(@PID)
pSessionControl\GetSessionIdentifier(@pSessionId)
pSessionControl\GetState(@state)
If pSessionId
fname = GetFilePart(StringField(StringField(PeekS(pSessionId, -1, #PB_Unicode), 2, "|"), 1, "%"))
EndIf
sessionEvents = CreateMyAudioSessionEvents(PID, pSessionId, pSessionControl)
pSessionControl\RegisterAudioSessionNotification(sessionEvents)
ProcedureReturn #S_OK
EndProcedure
Define deviceEnumerator.IMMDeviceEnumerator
Define defaultRender.IMMDevice
Define pSessionManager.IAudioSessionManager2
Define MyAudioSessionNotification.IAudioSessionNotification
Define pSessionList.IAudioSessionEnumerator
Define pSessionControl.IAudioSessionControl2
Define sessionEvents.IAudioSessionEvents
Define hr, cbSessionCount, index, PID, NewState, pSessionId
hr = CoInitializeEx_(#Null, #COINIT_APARTMENTTHREADED)
If hr <> #S_OK
Debug "CoInitializeEx_ in new thread failed, hr=" + Hex(hr)
EndIf
hr = CoCreateInstance_(?uuidof_MMDeviceEnumerator, #Null, #CLSCTX_INPROC_SERVER, ?uuidof_IMMDeviceEnumerator, @deviceEnumerator)
If hr <> #S_OK
Debug "CoCreateInstance_(MMDeviceEnumerator) failed, hr=" + Hex(hr)
EndIf
hr = deviceEnumerator\GetDefaultAudioEndpoint(0, 0, @defaultRender)
If hr <> #S_OK
Debug "GetDefaultAudioEndpoint(eRender) failed, hr=" + Hex(hr)
EndIf
If defaultRender\Activate(?IID_IAudioSessionManager, #CLSCTX_ALL, #Null, @pSessionManager) <> #S_OK
Debug "Activate IAudioSessionManager2 failed"
EndIf
pSessionManager\QueryInterface(?IID_IAudioSessionManager2, @pSessionManager)
If pSessionManager\GetSessionEnumerator(@pSessionList) <> #S_OK
Debug "Get Session List Error"
EndIf
MyAudioSessionNotification = CreateMyAudioSessionNotification()
If MyAudioSessionNotification = #Null
Debug "Failed to create IAudioSessionNotification COM object."
EndIf
hr = pSessionManager\RegisterSessionNotification(MyAudioSessionNotification)
If hr <> #S_OK
Debug "RegisterSessionNotification failed, hr=" + Hex(hr)
EndIf
If pSessionList\GetCount(@cbSessionCount) <> #S_OK
Debug "Get Session Count Error"
EndIf
For index = 0 To cbSessionCount - 1
pSessionList\GetSession(index, @pSessionControl)
If pSessionControl\QueryInterface(?IID_IAudioSessionControl2, @pSessionControl) = #S_OK
pSessionControl\GetProcessId(@PID)
pSessionControl\GetState(@NewState)
pSessionControl\GetSessionIdentifier(@pSessionId)
sessionEvents = CreateMyAudioSessionEvents(PID, pSessionId, pSessionControl)
pSessionControl\RegisterAudioSessionNotification(sessionEvents)
PostEvent(#EventStateActive, #PB_Ignore, #PB_Ignore, #PB_Ignore, sessionEvents)
EndIf
Next
DataSection
uuidof_MMDeviceEnumerator:
Data.l $BCDE0395
Data.w $E52F, $467C
Data.b $8E, $3D, $C4, $57, $92, $91, $69, $2E
uuidof_IMMDeviceEnumerator:
Data.l $A95664D2
Data.w $9614, $4F35
Data.b $A7, $46, $DE, $8D, $B6, $36, $17, $E6
IID_IAudioSessionManager:
Data.l $BFA971F1
Data.w $4D5E, $40BB
Data.b $93, $5E, $96, $70, $39, $BF, $BE, $E4
IID_IAudioSessionManager2:
Data.l $77AA99A0
Data.w $1BD6, $484F
Data.b $8B, $C7, $2C, $65, $4C, $9A, $9B, $6F
IID_IAudioSessionControl2:
Data.l $bfb7ff88
Data.w $7239, $4fc9
Data.b $8f, $a2, $07, $c9, $50, $be, $9c, $6d
EndDataSection
Code: Select all
IncludeFile "notifications_default_interface.pbi"
Enumeration #PB_Event_FirstCustomValue
#EventStateInactive
#EventStateActive
#EventStateExpired
EndEnumeration
Enumeration
#AudioSessionStateInactive
#AudioSessionStateActive
#AudioSessionStateExpired
EndEnumeration
Procedure CleanUp(*Object.MyAudioSessionEventsObj)
CoTaskMemFree_(*Object\SessionID)
FreeMemory(*Object\lpVtbl)
*Object\pSessionControl\Release()
MyRelease(*Object)
EndProcedure
Define Event, fname.s, *Object.MyAudioSessionEventsObj
OpenWindow(0, 0, 0, 0, 0, "", #PB_Window_Invisible)
Repeat
Event = WaitWindowEvent()
Select Event
Case #EventStateActive
*Object = EventData()
fname = GetFilePart(StringField(StringField(PeekS(*Object\SessionId, -1, #PB_Unicode), 2, "|"), 1, "%"))
Debug "Active: " + *Object\ProcessID + " " + fname
Case #EventStateInactive
*Object = EventData()
fname = GetFilePart(StringField(StringField(PeekS(*Object\SessionId, -1, #PB_Unicode), 2, "|"), 1, "%"))
Debug "Inactive: " + *Object\ProcessID + " " + fname
Case #EventStateExpired
*Object = EventData()
fname = GetFilePart(StringField(StringField(PeekS(*Object\SessionId, -1, #PB_Unicode), 2, "|"), 1, "%"))
Debug "Expired: " + *Object\ProcessID + " " + fname
CleanUp(*Object)
EndSelect
Until Event = #PB_Event_CloseWindow
19/20/2025 - Changed Quad types to Integer to support 32 bit.
20/02/2025 - Implemented UnregisterAudioSessionNotification on Expired events
05/03/2025 - Fixed memory leaks. Added custom events. Initialized COM properly to avoid crashes (thanks Zapman)