Skip to content

Commit

Permalink
Windows 10 native fixes - marshalling WaveFormats and PropVariants wi…
Browse files Browse the repository at this point in the history
…th IntPtrs
  • Loading branch information
markheath committed Mar 11, 2016
1 parent 1e10d67 commit 2d8deb6
Show file tree
Hide file tree
Showing 21 changed files with 243 additions and 136 deletions.
1 change: 1 addition & 0 deletions NAudio.Universal/NAudio.Universal.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .Net Framework and Windows SDK are automatically included -->
Expand Down
8 changes: 4 additions & 4 deletions NAudio.Win8/Wave/WaveInputs/WasapiCaptureRT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,13 @@ private void InitializeCaptureDevice(IAudioClient audioClientInterface)
var audioClient = new AudioClient((IAudioClient)audioClientInterface);
if (waveFormat == null)
{
this.waveFormat = audioClient.MixFormat;
waveFormat = audioClient.MixFormat;
}

long requestedDuration = REFTIMES_PER_MILLISEC * 100;


if (!audioClient.IsFormatSupported(AudioClientShareMode.Shared, WaveFormat))
if (!audioClient.IsFormatSupported(AudioClientShareMode.Shared, waveFormat))
{
throw new ArgumentException("Unsupported Wave Format");
}
Expand All @@ -133,7 +133,7 @@ private void InitializeCaptureDevice(IAudioClient audioClientInterface)
streamFlags,
requestedDuration,
0,
this.waveFormat,
waveFormat,
Guid.Empty);


Expand Down Expand Up @@ -161,7 +161,7 @@ public async void StartRecording()
{
stop = false;

var icbh = new ActivateAudioInterfaceCompletionHandler(ac2 =>{ InitializeCaptureDevice((IAudioClient)ac2);});
var icbh = new ActivateAudioInterfaceCompletionHandler(ac2 => InitializeCaptureDevice((IAudioClient)ac2));

IActivateAudioInterfaceAsyncOperation activationOperation;
// must be called on UI thread
Expand Down
6 changes: 4 additions & 2 deletions NAudio.Win8/Wave/WaveOutputs/WasapiOutRT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public void SetClientProperties(bool useHardwareOffload, AudioStreamCategory cat
{
audioClientProperties = new AudioClientProperties()
{
cbSize = (uint) Marshal.SizeOf(typeof (AudioClientProperties)),
cbSize = (uint) Marshal.SizeOf<AudioClientProperties>(),
bIsOffload = Convert.ToInt32(useHardwareOffload),
eCategory = category,
Options = options
Expand All @@ -107,11 +107,13 @@ private async Task Activate()
var icbh = new ActivateAudioInterfaceCompletionHandler(
ac2 =>
{

if (this.audioClientProperties != null)
{
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(this.audioClientProperties.Value));
Marshal.StructureToPtr(this.audioClientProperties.Value, p, false);
ac2.SetClientProperties(p);
Marshal.FreeHGlobal(p);
// TODO: consider whether we can marshal this without the need for AllocHGlobal
}

Expand Down Expand Up @@ -602,7 +604,7 @@ int Initialize(AudioClientShareMode shareMode,
int IsFormatSupported(
AudioClientShareMode shareMode,
[In] WaveFormat pFormat,
[Out, MarshalAs(UnmanagedType.LPStruct)] out WaveFormatExtensible closestMatchFormat);
out IntPtr closestMatchFormat);

int GetMixFormat(out IntPtr deviceFormatPointer);

Expand Down
23 changes: 21 additions & 2 deletions NAudio/CoreAudioApi/AudioClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using NAudio.CoreAudioApi.Interfaces;
using System.Runtime.InteropServices;
using NAudio.Utils;
using NAudio.Wave;

namespace NAudio.CoreAudioApi
Expand Down Expand Up @@ -233,6 +234,11 @@ public bool IsFormatSupported(AudioClientShareMode shareMode,
return IsFormatSupported(shareMode, desiredFormat, out closestMatchFormat);
}

private IntPtr GetPointerToPointer()
{
return Marshal.AllocHGlobal(MarshalHelpers.SizeOf<IntPtr>());
}

/// <summary>
/// Determines if the specified output format is supported in shared mode
/// </summary>
Expand All @@ -242,10 +248,22 @@ public bool IsFormatSupported(AudioClientShareMode shareMode,
/// <returns>True if the format is supported</returns>
public bool IsFormatSupported(AudioClientShareMode shareMode, WaveFormat desiredFormat, out WaveFormatExtensible closestMatchFormat)
{
int hresult = audioClientInterface.IsFormatSupported(shareMode, desiredFormat, out closestMatchFormat);
IntPtr pointerToPtr = GetPointerToPointer(); // IntPtr.Zero; // Marshal.AllocHGlobal(Marshal.SizeOf<WaveFormatExtensible>());
closestMatchFormat = null;
int hresult = audioClientInterface.IsFormatSupported(shareMode, desiredFormat, pointerToPtr);

var closestMatchPtr = MarshalHelpers.PtrToStructure<IntPtr>(pointerToPtr);

if (closestMatchPtr != IntPtr.Zero)
{
closestMatchFormat = MarshalHelpers.PtrToStructure<WaveFormatExtensible>(closestMatchPtr);
Marshal.FreeCoTaskMem(closestMatchPtr);
}
Marshal.FreeHGlobal(pointerToPtr);
// S_OK is 0, S_FALSE = 1
if (hresult == 0)
{

// directly supported
return true;
}
Expand All @@ -255,7 +273,8 @@ public bool IsFormatSupported(AudioClientShareMode shareMode, WaveFormat desired
}
if (hresult == (int)AudioClientErrors.UnsupportedFormat)
{
return false;
// Succeeded but the specified format is not supported in exclusive mode.
return shareMode != AudioClientShareMode.Exclusive;
}
Marshal.ThrowExceptionForHR(hresult);
// shouldn't get here
Expand Down
7 changes: 4 additions & 3 deletions NAudio/CoreAudioApi/AudioEndpointVolumeCallback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ 3. This notice may not be removed or altered from any source distribution.
using System;
using NAudio.CoreAudioApi.Interfaces;
using System.Runtime.InteropServices;
using NAudio.Utils;

namespace NAudio.CoreAudioApi
{
Expand All @@ -50,10 +51,10 @@ public void OnNotify(IntPtr notifyData)
//data is marshalled into the data structure, then with some IntPtr math the
//remaining floats are read from memory.
//
var data = (AudioVolumeNotificationDataStruct)Marshal.PtrToStructure(notifyData, typeof(AudioVolumeNotificationDataStruct));
var data = MarshalHelpers.PtrToStructure<AudioVolumeNotificationDataStruct>(notifyData);

//Determine offset in structure of the first float
var offset = Marshal.OffsetOf(typeof(AudioVolumeNotificationDataStruct), "ChannelVolume");
var offset = MarshalHelpers.OffsetOf<AudioVolumeNotificationDataStruct>("ChannelVolume");
//Determine offset in memory of the first float
var firstFloatPtr = (IntPtr)((long)notifyData + (long)offset);

Expand All @@ -62,7 +63,7 @@ public void OnNotify(IntPtr notifyData)
//Read all floats from memory.
for (int i = 0; i < data.nChannels; i++)
{
voldata[i] = (float)Marshal.PtrToStructure(firstFloatPtr, typeof(float));
voldata[i] = MarshalHelpers.PtrToStructure<float>(firstFloatPtr);
}

//Create combined structure and Fire Event in parent class.
Expand Down
11 changes: 6 additions & 5 deletions NAudio/CoreAudioApi/Interfaces/IAudioClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ namespace NAudio.CoreAudioApi.Interfaces
/// Defined in AudioClient.h
/// </summary>
[Guid("1CB9AD4C-DBFA-4c32-B178-C2F568A703B2"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
ComImport]
internal interface IAudioClient
{
[PreserveSig]
int Initialize(AudioClientShareMode shareMode,
AudioClientStreamFlags StreamFlags,
AudioClientStreamFlags streamFlags,
long hnsBufferDuration, // REFERENCE_TIME
long hnsPeriodicity, // REFERENCE_TIME
[In] WaveFormat pFormat,
[In] ref Guid AudioSessionGuid);
[In] ref Guid audioSessionGuid);

/// <summary>
/// The GetBufferSize method retrieves the size (maximum capacity) of the endpoint buffer.
Expand All @@ -34,8 +35,8 @@ int Initialize(AudioClientShareMode shareMode,
int IsFormatSupported(
AudioClientShareMode shareMode,
[In] WaveFormat pFormat,
[Out, MarshalAs(UnmanagedType.LPStruct)] out WaveFormatExtensible closestMatchFormat);
IntPtr closestMatchFormat);

int GetMixFormat(out IntPtr deviceFormatPointer);

// REFERENCE_TIME is 64 bit int
Expand Down
5 changes: 2 additions & 3 deletions NAudio/CoreAudioApi/Interfaces/IAudioRenderClient.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace NAudio.CoreAudioApi.Interfaces
{
[Guid("F294ACFC-3146-4483-A7BF-ADDCA7C260E2"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
ComImport]
interface IAudioRenderClient
{
int GetBuffer(int numFramesRequested, out IntPtr dataBufferPointer);
Expand Down
21 changes: 17 additions & 4 deletions NAudio/CoreAudioApi/PropVariant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ 3. This notice may not be removed or altered from any source distribution.
using System;
using System.IO;
using System.Runtime.InteropServices;
using NAudio.Utils;

namespace NAudio.CoreAudioApi.Interfaces
{
Expand Down Expand Up @@ -191,7 +192,7 @@ public object Value
case VarEnum.VT_VECTOR | VarEnum.VT_UI1:
return GetBlob();
case VarEnum.VT_CLSID:
return (Guid)Marshal.PtrToStructure(pointerValue, typeof(Guid));
return MarshalHelpers.PtrToStructure<Guid>(pointerValue);
case VarEnum.VT_BOOL:
switch (boolVal)
{
Expand All @@ -203,19 +204,31 @@ public object Value
throw new NotSupportedException("PropVariant VT_BOOL must be either -1 or 0");
}
}
throw new NotImplementedException("PropVariant " + ve.ToString());
throw new NotImplementedException("PropVariant " + ve);
}
}

/// <summary>
/// allows freeing up memory, might turn this into a Dispose method?
/// </summary>
[Obsolete("Call with pointer instead")]
public void Clear()
{
PropVariantClear(ref this);
var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(this));
Marshal.StructureToPtr(this, ptr, false);
PropVariantClear(ptr);
Marshal.FreeHGlobal(ptr);
}

/// <summary>
/// Clears with a known pointer
/// </summary>
public static void Clear(IntPtr ptr)
{
PropVariantClear(ptr);
}

[DllImport("ole32.dll")]
private static extern int PropVariantClear(ref PropVariant pvar);
private static extern int PropVariantClear(IntPtr pvar);
}
}
12 changes: 5 additions & 7 deletions NAudio/MediaFoundation/IMFActivate.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using NAudio.CoreAudioApi.Interfaces;

namespace NAudio.MediaFoundation
{
Expand All @@ -15,7 +13,7 @@ public interface IMFActivate : IMFAttributes
/// <summary>
/// Retrieves the value associated with a key.
/// </summary>
new void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, Out] ref PropVariant pValue);
new void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, Out] IntPtr pValue);

/// <summary>
/// Retrieves the data type of the value associated with a key.
Expand All @@ -25,12 +23,12 @@ public interface IMFActivate : IMFAttributes
/// <summary>
/// Queries whether a stored attribute value equals a specified PROPVARIANT.
/// </summary>
new void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
new void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

/// <summary>
/// Compares the attributes on this object with the attributes on another object.
/// </summary>
new void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, int MatchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
new void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, int matchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

/// <summary>
/// Retrieves a UINT32 value associated with a key.
Expand Down Expand Up @@ -94,7 +92,7 @@ public interface IMFActivate : IMFAttributes
/// <summary>
/// Associates an attribute value with a key.
/// </summary>
new void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value);
new void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr value);

/// <summary>
/// Removes a key/value pair from the object's attribute list.
Expand Down Expand Up @@ -160,7 +158,7 @@ public interface IMFActivate : IMFAttributes
/// <summary>
/// Retrieves an attribute at the specified index.
/// </summary>
new void GetItemByIndex(int unIndex, out Guid pGuidKey, [In, Out] ref PropVariant pValue);
new void GetItemByIndex(int unIndex, out Guid pGuidKey, [In, Out] IntPtr pValue);

/// <summary>
/// Copies all of the attributes from this object into another attribute store.
Expand Down
8 changes: 4 additions & 4 deletions NAudio/MediaFoundation/IMFAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public interface IMFAttributes
/// <summary>
/// Retrieves the value associated with a key.
/// </summary>
void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, Out] ref PropVariant pValue);
void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, Out] IntPtr pValue);

/// <summary>
/// Retrieves the data type of the value associated with a key.
Expand All @@ -25,12 +25,12 @@ public interface IMFAttributes
/// <summary>
/// Queries whether a stored attribute value equals a specified PROPVARIANT.
/// </summary>
void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

/// <summary>
/// Compares the attributes on this object with the attributes on another object.
/// </summary>
void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, int MatchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, int matchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

/// <summary>
/// Retrieves a UINT32 value associated with a key.
Expand Down Expand Up @@ -160,7 +160,7 @@ void SetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalA
/// <summary>
/// Retrieves an attribute at the specified index.
/// </summary>
void GetItemByIndex(int unIndex, out Guid pGuidKey, [In, Out] ref PropVariant pValue);
void GetItemByIndex(int unIndex, out Guid pGuidKey, [In, Out] IntPtr pValue);

/// <summary>
/// Copies all of the attributes from this object into another attribute store.
Expand Down
13 changes: 6 additions & 7 deletions NAudio/MediaFoundation/IMFMediaEvent.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using NAudio.CoreAudioApi.Interfaces;
Expand All @@ -17,7 +16,7 @@ public interface IMFMediaEvent : IMFAttributes
/// <summary>
/// Retrieves the value associated with a key.
/// </summary>
new void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, Out] ref PropVariant pValue);
new void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, Out] IntPtr pValue);

/// <summary>
/// Retrieves the data type of the value associated with a key.
Expand All @@ -27,12 +26,12 @@ public interface IMFMediaEvent : IMFAttributes
/// <summary>
/// Queries whether a stored attribute value equals a specified PROPVARIANT.
/// </summary>
new void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
new void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

/// <summary>
/// Compares the attributes on this object with the attributes on another object.
/// </summary>
new void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, int MatchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
new void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, int matchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);

/// <summary>
/// Retrieves a UINT32 value associated with a key.
Expand Down Expand Up @@ -96,7 +95,7 @@ public interface IMFMediaEvent : IMFAttributes
/// <summary>
/// Associates an attribute value with a key.
/// </summary>
new void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value);
new void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr value);

/// <summary>
/// Removes a key/value pair from the object's attribute list.
Expand Down Expand Up @@ -162,7 +161,7 @@ public interface IMFMediaEvent : IMFAttributes
/// <summary>
/// Retrieves an attribute at the specified index.
/// </summary>
new void GetItemByIndex(int unIndex, out Guid pGuidKey, [In, Out] ref PropVariant pValue);
new void GetItemByIndex(int unIndex, out Guid pGuidKey, [In, Out] IntPtr pValue);

/// <summary>
/// Copies all of the attributes from this object into another attribute store.
Expand Down Expand Up @@ -203,6 +202,6 @@ public interface IMFMediaEvent : IMFAttributes
/// virtual HRESULT STDMETHODCALLTYPE GetValue(
/// /* [out] */ __RPC__out PROPVARIANT *pvValue) = 0;
/// </remarks>
void GetValue(out PropVariant pvValue);
void GetValue([Out]IntPtr pvValue);
}
}
Loading

0 comments on commit 2d8deb6

Please sign in to comment.