A whole bunch of changes and the addition of the code for session events.

At this point it basically works to:
	Initialise everything for the audio session
	Get the default mm/endpoint device
	Get a list of all the currently open audio sessions
	Register an events interface for the sessions
	React to new sessions by adding them to the list
	React to session events, currently just with print statements
This commit is contained in:
Thomas Wilkie 2023-09-24 21:51:30 +09:00
parent cb4007f7c3
commit 772a1dbf4c
9 changed files with 259 additions and 71 deletions

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
Debug/
x64/
.vs/
Notes.txt
Notes.txt

126
AudioSessions_framework.c Normal file
View File

@ -0,0 +1,126 @@
#include "AudioSesssions_framework.h"
static inline HRESULT getDefaultAudioEndpoint(AudioSessionsInformation*);
static inline HRESULT refreshIMMEnumerator(AudioSessionsInformation*);
static HRESULT releaseControlInterfaces(AudioSessionsInformation*);
/*
* \brief Initilises Co in multithreaded and gets IMM devices & default device interfaces.
*/
HRESULT initialiseSessionsFramework(AudioSessionsInformation * pSessionInformation)
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);
// Gets a new enumerator of all the multimedia devices
refreshIMMEnumerator(pSessionInformation);
// Gets the device interface for the default audio endpoint
getDefaultAudioEndpoint(pSessionInformation);
InitializeAudioSessionNotification(&pSessionInformation->AudioSessionNotification,
&pSessionInformation->AudioSessionControlsCollection,
&pSessionInformation->NumberAudioSessionsActive);
refreshOpenSessions(pSessionInformation);
return S_OK;
}
HRESULT refreshOpenSessions(AudioSessionsInformation* pSessionInformation)
{
// Throw an error if the IMMDevice pointer has not been initialised
if (!pSessionInformation->IMMDevice) return CO_E_NOTINITIALIZED;
// Get a session manager 2 interface and use it to get a session enumerator
IAudioSessionManager2* myAudioSessionManager2 = NULL;
pSessionInformation->IMMDevice->lpVtbl->Activate(pSessionInformation->IMMDevice,
&IID_IAudioSessionManager2,
CLSCTX_ALL,
NULL,
&myAudioSessionManager2);
IAudioSessionEnumerator* myAudioSessionEnumerator;
myAudioSessionManager2->lpVtbl->GetSessionEnumerator(myAudioSessionManager2,
&myAudioSessionEnumerator);
// Then release the notifications interface
pSessionInformation->AudioSessionNotification->lpVtbl->Release(pSessionInformation->AudioSessionNotification);
// And register a new one, I think this may be necessary if the endpoint has changed
myAudioSessionManager2->lpVtbl->RegisterSessionNotification(myAudioSessionManager2, pSessionInformation->AudioSessionNotification);
// Once we have the number of open sessions memory can be allocated for the control interfaces
// First; release any control interfaces left over and free the memory
releaseControlInterfaces(pSessionInformation);
// Then get the number of sessions active and save it in the session information stuct
myAudioSessionEnumerator->lpVtbl->GetCount(myAudioSessionEnumerator,
&pSessionInformation->NumberAudioSessionsActive);
// Allocate memory
pSessionInformation->AudioSessionControlsCollection = malloc(sizeof(IAudioSessionControl) * pSessionInformation->NumberAudioSessionsActive);
// Print to console if the memory cannot be allocated
if (!pSessionInformation->AudioSessionControlsCollection) printf("\nMemory allocation error.");
// Put all the sessions into the array along with an index for each, also register the events
for (int i = 0; i < pSessionInformation->NumberAudioSessionsActive; i++)
{
//TODO: This should really be a simple audio interface rather then an audio session control interface
IAudioSessionControl* sessionControl;
// Get the session control interface from the enumerator
myAudioSessionEnumerator->lpVtbl->GetSession(myAudioSessionEnumerator, i, &sessionControl);
pSessionInformation->AudioSessionControlsCollection[i] = sessionControl;
// Register the events interface for the session
// This is complicated slightly because I want to be able to identify the disconnected session in the disconnection handler
// This requires putting the address of the control interface into the events interface
// The problem with this is that there is an events interface created for each control session
// Although they all share a virtual table
myIAudioSessionEvents* AudioSessionEvents;
// The virtual table will be created on the first call to this function and then used for all future calls
// Also takes the address of the pointer to the array of control interface pointers
// This address should be constant whereas the pointer to the array of interface pointers will change every time an interface is add/destroyed and new memory is allocated
InitializeAudioSessionEvents(&AudioSessionEvents, sessionControl, pSessionInformation->AudioSessionControlsCollection);
// Now the events interface has been setup, register it
// This requires casting my session events interface to the interface expected by the api
sessionControl->lpVtbl->RegisterAudioSessionNotification(sessionControl, AudioSessionEvents);
printf("\nRegistered session %d with control iterface at %p and events interface at %p", i, sessionControl, &AudioSessionEvents);
}
// Release the session manager and enumerator
myAudioSessionManager2->lpVtbl->Release(myAudioSessionManager2);
myAudioSessionEnumerator->lpVtbl->Release(myAudioSessionEnumerator);
return S_OK;
}
// Releases the audio control interfaces in the session information structure.
// Also releases the memory allocated to the array of pointers to the control interfaces
HRESULT releaseControlInterfaces(AudioSessionsInformation * pSessionInformation)
{
// Goes through and releases all the control interfaces left over.
for (int i = 0; i < pSessionInformation->NumberAudioSessionsActive; i++)
{
//ControlInterfaces[i]->lpVtbl->Release(ControlInterfaces[i]);
IAudioSessionControl* nextInterface = (pSessionInformation->AudioSessionControlsCollection[i]);
nextInterface->lpVtbl->Release(nextInterface);
}
if (!pSessionInformation->AudioSessionControlsCollection) free(pSessionInformation->AudioSessionControlsCollection);
return S_OK;
}
// Updates the IMM Device default audio endpoint in the session information structure
HRESULT getDefaultAudioEndpoint(AudioSessionsInformation* pSessionInformation)
{
return pSessionInformation->IMMEnumerator->lpVtbl->GetDefaultAudioEndpoint(pSessionInformation->IMMEnumerator,
eRender,
eMultimedia,
&(pSessionInformation->IMMDevice));
}
// Refreshes the list of IMM devices
HRESULT refreshIMMEnumerator(AudioSessionsInformation* pSessionInformation)
{
return CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, &(pSessionInformation->IMMEnumerator));
}

View File

@ -0,0 +1,36 @@
#ifndef _AUDIOSESSIONS_FRAMEWORK_
#define _AUDIOSESSIONS_FRAMEWORK_
/*
* The intention of this file is to add an abstraction layer between the main program and the Core Audio API
*
* Functions are:
* Ensure there is always an updated accurate list of all audio sessions active on the device.
* Supply a single struct allowing for simple function signatures and easy access to important information
*/
#include <Windows.h>
#include <stdio.h>
#include <combaseapi.h>
#include <mmdeviceapi.h>
#include <audiopolicy.h>
#include "IAudioSessionNotification.h"
#include "IAudioSessionEvents.h"
#include "CLSIDs_and_IIDs.h"
typedef struct AudioSessionsInformation AudioSessionsInformation;
HRESULT initialiseSessionsFramework(AudioSessionsInformation*);
HRESULT refreshOpenSessions(AudioSessionsInformation*);
/*
* \brief Structure containing base interfaces needed for other framework calls
*/
struct AudioSessionsInformation {
IAudioSessionControl** AudioSessionControlsCollection;
int NumberAudioSessionsActive;
IMMDeviceEnumerator* IMMEnumerator;
IMMDevice* IMMDevice;
IAudioSessionNotification * AudioSessionNotification;
};
#endif

View File

@ -1,46 +1,65 @@
#include "IAudioSessionEvents.h"
HRESULT InitializeAudioSessionEvents(IAudioSessionEvents** this)
static IAudioSessionEventsVtbl* pvtbl = NULL;
static HRESULT CreateVTBL(void);
// Creates a session events interface and gives it the relevant information
HRESULT InitializeAudioSessionEvents(myIAudioSessionEvents** this, IAudioSessionControl * pParentInterface, IAudioSessionControl *** pControlInterfaceArrayAddress)
{
// If the vtbl of all the events functions hasn't already been created then make it now
// Return the error from the vtbl creation if there is a problem
HRESULT result = S_OK;
if (!pvtbl) result = CreateVTBL();
if (result) return result;
// The VTBL is constant for all events interfaces
// There are different interfaces with different parent interfaces and refernce numbers
//Assign memory for the events struct. Cast the pointer returned from malloc to the correct type
myIAudioSessionEvents* AudioSessionEvents;
AudioSessionEvents = (myIAudioSessionEvents*) malloc(sizeof(myIAudioSessionEvents));
//printf("\nMalloc: %p", AudioSessionEvents);
if (!AudioSessionEvents)
{
return E_OUTOFMEMORY;
}
// Add the virtual table to the strut
AudioSessionEvents->lpVtbl = pvtbl;
AudioSessionEvents->cRef = 0;
AudioSessionEvents->pParentAudioSessionControl = pParentInterface;
AudioSessionEvents->pControlInterfaceArrayAddress = pControlInterfaceArrayAddress;
*this = AudioSessionEvents;
//printf("\nSession events: %p", *this);
return S_OK;
}
HRESULT CreateVTBL(void)
{
// Initializes the IAudioSessionNotification struct, this involves creating it, allocating memory, setting initial vaules and assigning the functions to be used
//Allocate memory for the vtable
IAudioSessionEventsVtbl* vtbl = malloc(sizeof(IAudioSessionEventsVtbl));
pvtbl = malloc(sizeof(IAudioSessionEventsVtbl));
if (!vtbl)
if (!pvtbl)
{
// If the vtable memory cannot be assigned
return E_OUTOFMEMORY;
}
// Asign all the functions
vtbl->AddRef = (*IAudioSessionEventsAddRef);
vtbl->QueryInterface = (*IAudioSessionEventsQueryInterface);
vtbl->Release = (*IAudioSessionEventsRelease);
vtbl->OnChannelVolumeChanged = (*IAudioSessionEventsOnChannelVolumeChanged);
vtbl->OnDisplayNameChanged = (*IAudioSessionEventsOnDisplayNameChanged);
vtbl->OnGroupingParamChanged = (*IAudioSessionEventsOnGroupingParamChanged);
vtbl->OnIconPathChanged = (*IAudioSessionEventsOnIconPathChanged);
vtbl->OnSessionDisconnected = (*IAudioSessionEventsOnSessionDisconnected);
vtbl->OnSimpleVolumeChanged = (*IAudioSessionEventsOnSimpleVolumeChanged);
vtbl->OnStateChanged = (*IAudioSessionEventsOnStateChanged);
//Assign memory for the notifications struct. Cast the pointer returned from malloc to the correct type ??
myIAudioSessionEvents* AudioSessionEvents;
AudioSessionEvents = (myIAudioSessionEvents*) malloc(sizeof(myIAudioSessionEvents));
if (!AudioSessionEvents)
{
// If the struct memory cannot be assigned free the memory previously assigned to the vtable and return
free(vtbl);
return E_OUTOFMEMORY;
}
// Add the virtual table to the strut
AudioSessionEvents->lpVtbl = vtbl;
AudioSessionEvents->cRef = 0;
// Update the pointer address passed into the function
*this = (*IAudioSessionEvents) AudioSessionEvents;
pvtbl->AddRef = (*IAudioSessionEventsAddRef);
pvtbl->QueryInterface = (*IAudioSessionEventsQueryInterface);
pvtbl->Release = (*IAudioSessionEventsRelease);
pvtbl->OnChannelVolumeChanged = (*IAudioSessionEventsOnChannelVolumeChanged);
pvtbl->OnDisplayNameChanged = (*IAudioSessionEventsOnDisplayNameChanged);
pvtbl->OnGroupingParamChanged = (*IAudioSessionEventsOnGroupingParamChanged);
pvtbl->OnIconPathChanged = (*IAudioSessionEventsOnIconPathChanged);
pvtbl->OnSessionDisconnected = (*IAudioSessionEventsOnSessionDisconnected);
pvtbl->OnSimpleVolumeChanged = (*IAudioSessionEventsOnSimpleVolumeChanged);
pvtbl->OnStateChanged = (*IAudioSessionEventsOnStateChanged);
//printf("\nEvents VTBL at %p", pvtbl);
return S_OK;
}
@ -102,7 +121,7 @@ ULONG STDMETHODCALLTYPE IAudioSessionEventsRelease(IAudioSessionEvents* this)
HRESULT IAudioSessionEventsOnChannelVolumeChanged(IAudioSessionEvents* this, DWORD ChannelCount, float* NewChannelVolumeArray, DWORD ChangedChannel, LPCGUID EventContext)
{
printf("\nChannel &ld volume changed to %f.3", ChangedChannel, NewChannelVolumeArray + ChangedChannel);
printf("\nChannel %d volume changed to %f.3", ChangedChannel, NewChannelVolumeArray[ChangedChannel]);
return S_OK;
}
@ -127,7 +146,11 @@ HRESULT IAudioSessionEventsOnIconPathChanged(IAudioSessionEvents* this, LPCWSTR
HRESULT IAudioSessionEventsOnSessionDisconnected(IAudioSessionEvents* this, AudioSessionDisconnectReason DisconnectReason)
{
printf("\nSession disconnect reason &d", (int) DisconnectReason);
myIAudioSessionEvents* castInterface = (myIAudioSessionEvents*)this;
printf("\nSession with control interface at %p disconnected.", castInterface->pParentAudioSessionControl);
// Delete the unused control interface from the array of interfaces
return S_OK;
}

View File

@ -12,7 +12,7 @@ typedef interface myIAudioSessionEvents myIAudioSessionEvents;
#endif /* __myIAudioSessionEvents_FWD_DEFINED__ */
HRESULT InitializeAudioSessionEvents(IAudioSessionEvents**);
HRESULT InitializeAudioSessionEvents(myIAudioSessionEvents **, IAudioSessionControl *, IAudioSessionControl ***);
HRESULT STDMETHODCALLTYPE IAudioSessionEventsQueryInterface(IAudioSessionEvents*, REFIID, void**);
ULONG STDMETHODCALLTYPE IAudioSessionEventsAddRef(IAudioSessionEvents*);
@ -26,12 +26,14 @@ HRESULT IAudioSessionEventsOnSimpleVolumeChanged(IAudioSessionEvents*, float, BO
HRESULT IAudioSessionEventsOnStateChanged(IAudioSessionEvents*, AudioSessionState);
interface myIAudioSessionEvents
{
interface myIAudioSessionEvents {
CONST_VTBL struct IAudioSessionEventsVtbl* lpVtbl;
ULONG cRef;
// This is an extension to the standard interface and will be used to keep track of the IAudioSessionControl interface which created it
IAudioSessionControl* pParentAudioSessionControl;
// This is the address of the pointer which points to the array of control interface pointers
// This constant as far as this interface is concerned but is required for finding the array of interfaces on session destruction
IAudioSessionControl*** pControlInterfaceArrayAddress;
};
#endif

View File

@ -4,19 +4,19 @@
{
// Initializes the IAudioSessionNotification struct, this involves creating it, allocating memory, setting initial vaules and assigning the functions to be used
//Allocate memory for the vtable
IAudioSessionNotificationVtbl* vtbl = malloc(sizeof(IAudioSessionNotificationVtbl));
IAudioSessionNotificationVtbl* pvtbl = malloc(sizeof(IAudioSessionNotificationVtbl));
if (!vtbl)
if (!pvtbl)
{
// If the vtable memory cannot be assigned
return E_OUTOFMEMORY;
}
// Asign all the functions
vtbl->AddRef = (*IAudioSessionNotificationAddRef);
vtbl->OnSessionCreated = (*IAudioSessionNotificationOnSessionCreated);
vtbl->QueryInterface = (*IAudioSessionNotificationQueryInterface);
vtbl->Release = (*IAudioSessionNotificationRelease);
pvtbl->AddRef = (*IAudioSessionNotificationAddRef);
pvtbl->OnSessionCreated = (*IAudioSessionNotificationOnSessionCreated);
pvtbl->QueryInterface = (*IAudioSessionNotificationQueryInterface);
pvtbl->Release = (*IAudioSessionNotificationRelease);
//Assign memory for the notifications struct. Cast the pointer returned from malloc to the correct type
myIAudioSessionNotification * AudioSessionNotification;
@ -24,13 +24,13 @@
if (!AudioSessionNotification)
{
// If the struct memory cannot be assigned free the memory previously assigned to the vtable and return
free(vtbl);
free(pvtbl);
return E_OUTOFMEMORY;
}
// Add the virtual table to the strut
AudioSessionNotification->lpVtbl = vtbl;
AudioSessionNotification->lpVtbl = pvtbl;
AudioSessionNotification->cRef = 0;
// pppAudioSessionControlInterfaces is a pointer to an array of control interface pointers
AudioSessionNotification->pppAudioSessionControlInterfaces = pppAudioSessionControlInterfaces;
@ -97,24 +97,13 @@ ULONG STDMETHODCALLTYPE IAudioSessionNotificationAddRef(IAudioSessionNotificatio
return InterlockedIncrement(&castInterface->cRef);
}
HRESULT IAudioSessionNotificationOnSessionCreated(IAudioSessionNotification* this, IAudioSessionControl* pNewSession)
HRESULT IAudioSessionNotificationOnSessionCreated(IAudioSessionNotification* this, IAudioSessionControl* pNewSessionControl)
{
printf("\nNew session created");
// Simpler version with fixed size array of pointers declared in the main thread and put onto the stack.
myIAudioSessionNotification* castInterface = (myIAudioSessionNotification*)this;
int numberInterfaces = *(castInterface->pNumberAudioSessionControls);
IAudioSessionControl ** audioSessionControlArray = castInterface->pppAudioSessionControlInterfaces + numberInterfaces;
*audioSessionControlArray = pNewSession;
*(castInterface->pNumberAudioSessionControls) = numberInterfaces + 1;
return S_OK;
// First thing is to add a ref to the control interface so it isn't released by the system
pNewSessionControl->lpVtbl->AddRef(pNewSessionControl);
/*
*
* This is using realloc and malloc to dynamically change the size of the array of pointers to interfaces.
* It doesn't work, I don't know why at this point.
*
// Add the new session to the array of IAudioSessionControl interfaces
// A pointer to pointer to the array is stored in the interface, along with a pointer to the number of current interfaces
// Cast IAudioSessionNotification to my implementation to access those variables
@ -124,11 +113,6 @@ HRESULT IAudioSessionNotificationOnSessionCreated(IAudioSessionNotification* thi
// The number of currently stored IAudioSessionControl interfaces
int numberOfInterfaces = *pCastInterface->pNumberAudioSessionControls;
if (numberOfInterfaces)
{
printf("");
}
// This is a pointer to the first element of the array of pointers to session control objects
IAudioSessionControl** pNewArray = NULL;
@ -154,7 +138,7 @@ HRESULT IAudioSessionNotificationOnSessionCreated(IAudioSessionNotification* thi
}
// At this point the memory has been allocated through realloc or malloc and the last pointer needs to be added to the array at the final location
*(pNewArray + numberOfInterfaces) = pNewSession;
*(pNewArray + numberOfInterfaces) = pNewSessionControl;
// Send the pointer to the new array back up the chain so it is seen by the main program thread
*(pCastInterface->pppAudioSessionControlInterfaces) = pNewArray;
@ -163,7 +147,9 @@ HRESULT IAudioSessionNotificationOnSessionCreated(IAudioSessionNotification* thi
*(pCastInterface->pNumberAudioSessionControls) = numberOfInterfaces + 1;
// The new IAudioSessionControl interface should now have been appended to the array
// Final step is to register the sesssion events
IAudioSessionEvents AudioSessionEvents;
//InitializeAudioSessionEvents(&AudioSessionEvents, pNewSessionControl, pSessionInformation->AudioSessionControlsCollection);
return S_OK;
*/
}

View File

@ -20,13 +20,11 @@ HRESULT IAudioSessionNotificationOnSessionCreated(IAudioSessionNotification*, IA
//HRESULT InitializeAudioSessionNotification(IAudioSessionNotification**, IAudioSessionControl***, int*);
HRESULT InitializeAudioSessionNotification(IAudioSessionNotification**, IAudioSessionControl***, int*);
interface myIAudioSessionNotification
{
interface myIAudioSessionNotification {
CONST_VTBL struct IAudioSessionNotificationVtbl* lpVtbl;
ULONG cRef;
const IAudioSessionControl *** pppAudioSessionControlInterfaces;
int * pNumberAudioSessionControls;
};
#endif

View File

@ -127,10 +127,14 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="AudioSessions_framework.c" />
<ClCompile Include="AudioSesssions_framework.h" />
<ClCompile Include="IAudioSessionEvents.c" />
<ClCompile Include="IAudioSessionNotification.c" />
<ClCompile Include="main.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="IAudioSessionEvents.h" />
<ClInclude Include="IAudioSessionNotification.h" />
<ClInclude Include="CLSIDs_and_IIDs.h" />
</ItemGroup>

View File

@ -21,6 +21,15 @@
<ClCompile Include="IAudioSessionNotification.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="AudioSessions_framework.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="AudioSesssions_framework.h">
<Filter>Header Files</Filter>
</ClCompile>
<ClCompile Include="IAudioSessionEvents.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="CLSIDs_and_IIDs.h">
@ -29,5 +38,8 @@
<ClInclude Include="IAudioSessionNotification.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IAudioSessionEvents.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>