OK, here's the solution. Maybe it is helpful for anybody who's trying to achieve the same with C#.
In my solution I'm gonna use the
IAudioClient
instead of
IAudioSessionManager
. I don't know whether it will work for the session manager, too - I didn't try but I guess so. I managed to get it working before I fixed the actual issue.
Edit.
If I could vote down my own solution, I would have done it ;)
While I was figuring out what's wrong I changed some lines of code. Turning out that I was setting the volume in different parts of my solution. Very clever. That were the reason for this "loops".
So, forget about the first solution. I updated the code below. All you have to do is listen to the VolumeChanged event. When and only when it's fired, update your UI.
First we need a
WAVEFORMATEX
structure. I don't know why I initially missed that part, it is well documented in MSDN.
[StructLayout(LayoutKind.Sequential)]
private struct WaveFormatEx
{
public ushort wFormatTag;
public ushort nChannels;
public uint nSamplesPerSec;
public uint nAvgBytesPerSec;
public ushort nBlockAlign;
public ushort wBitsPerSample;
public ushort cbSize;
};
We gonna use that structure to create an instance of IAudioClient.
The new ctor of class
VolumeManager
looks like this:
public VolumeManager()
{
var type = Type.GetTypeFromCLSID( Guid.Parse( ComCLSIDs.MMDeviceEnumeratorCLSID ) );
IMMDeviceEnumerator deviceEnumerator = (IMMDeviceEnumerator)Activator.CreateInstance( type );
IMMDevice device;
Marshal.ThrowExceptionForHR(
deviceEnumerator.GetDefaultAudioEndpoint( EDataFlow.eRender, ERole.eMultimedia, out device ) );
const uint CLSCTX_ALL = 0x1 | 0x2 | 0x4 | 0x10;
object obj;
Marshal.ThrowExceptionForHR(
device.Activate( Guid.Parse( ComIIDs.IAudioClientIID ), CLSCTX_ALL, IntPtr.Zero, out obj ) );
IAudioClient client = (IAudioClient)obj;
IntPtr waveFormatPtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof(WaveFormatEx) ) );
Marshal.ThrowExceptionForHR( client.GetMixFormat( out waveFormatPtr ) );
WaveFormatEx waveFormat = (WaveFormatEx)Marshal.PtrToStructure( waveFormatPtr, typeof( WaveFormatEx ) );
int hr = client.Initialize(
AUDCLNT_SHAREMODE.AUDCLNT_SHAREMODE_SHARED, 0, 10000000, 0, waveFormatPtr );
const int AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED = 1 << 31 | 0x889 << 16 | 0x19;
if ( hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED )
{
uint bufferSize;
client.GetBufferSize( out bufferSize );
ulong refTime = (ulong)( ( 10000.0 * 1000 / waveFormat.nSamplesPerSec * bufferSize ) + 0.5 );
Marshal.FinalReleaseComObject( client );
Marshal.FreeHGlobal( waveFormatPtr );
Marshal.ThrowExceptionForHR(
device.Activate( Guid.Parse( ComIIDs.IAudioClientIID ), CLSCTX_ALL, IntPtr.Zero, out obj ) );
client = (IAudioClient)obj;
waveFormatPtr = Marshal.AllocHGlobal( Marshal.SizeOf( typeof( WaveFormatEx ) ) );
Marshal.ThrowExceptionForHR( client.GetMixFormat( out waveFormatPtr ) );
Marshal.ThrowExceptionForHR(
client.Initialize( AUDCLNT_SHAREMODE.AUDCLNT_SHAREMODE_SHARED, 0, refTime, 0, waveFormatPtr ) );
}
else
{
Marshal.ThrowExceptionForHR( hr );
}
Marshal.ThrowExceptionForHR( client.GetService( Guid.Parse( ComIIDs.IAudioSessionControlIID ), out obj ) );
_sessionControl = (IAudioSessionControl)obj;
Marshal.ThrowExceptionForHR( client.GetService( Guid.Parse( ComIIDs.ISimpleAudioVolumeIID ), out obj ) );
_simpleVolume = (ISimpleAudioVolume)obj;
Marshal.ThrowExceptionForHR( Marshal.FinalReleaseComObject( deviceEnumerator ) );
Marshal.ThrowExceptionForHR( Marshal.FinalReleaseComObject( device ) );
Marshal.ThrowExceptionForHR( Marshal.FinalReleaseComObject( client ) );
Marshal.FreeHGlobal( waveFormatPtr );
_eventClient = new EventClient();
Marshal.ThrowExceptionForHR( _sessionControl.RegisterAudioSessionNotification( _eventClient ) );
_eventClient.DisplayNameChanged += OnDisplayNameChanged;
_eventClient.IconPathChanged += OnIconPathChanged;
_eventClient.VolumeChanged += OnVolumeChanged;
}
So far for the
VolumeManager
. Then some changes within
EventClient
.
public int OnSimpleVolumeChanged( float volume, bool isMuted, ref Guid eventContext )
{
if ( VolumeChanged != null && eventContext != Guid.Empty )
{
var args = new VolumeEventArgs( volume, isMuted, eventContext );
VolumeChanged( this, args );
}
return 0;
}
That's it ;)