Skip to content

Commit

Permalink
[.NET/AudioUnit] Use [UnmanagedCallersOnly] instead of [MonoPInvokeCa…
Browse files Browse the repository at this point in the history
…llback] Partial Fix for #10470 (#15808)
  • Loading branch information
stephen-hawley authored Sep 6, 2022
1 parent ea60486 commit 1efe5ff
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 5 deletions.
69 changes: 67 additions & 2 deletions src/AudioUnit/AUGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,15 @@ public AudioUnitStatus AddRenderNotify (RenderDelegate callback)
ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (callback));

AudioUnitStatus error = AudioUnitStatus.OK;
#if NET
unsafe {
if (graphUserCallbacks.Count == 0)
error = (AudioUnitStatus) AUGraphAddRenderNotify (Handle, &renderCallback, GCHandle.ToIntPtr (gcHandle));
}
#else
if (graphUserCallbacks.Count == 0)
error = (AudioUnitStatus) AUGraphAddRenderNotify (Handle, renderCallback, GCHandle.ToIntPtr (gcHandle));
#endif

if (error == AudioUnitStatus.OK)
graphUserCallbacks.Add (callback);
Expand All @@ -156,22 +163,50 @@ public AudioUnitStatus RemoveRenderNotify (RenderDelegate callback)
throw new ArgumentException ("Cannot unregister a callback that has not been registered");

AudioUnitStatus error = AudioUnitStatus.OK;
#if NET
unsafe {
if (graphUserCallbacks.Count == 0)
error = (AudioUnitStatus)AUGraphRemoveRenderNotify (Handle, &renderCallback, GCHandle.ToIntPtr (gcHandle));
}
#else
if (graphUserCallbacks.Count == 0)
error = (AudioUnitStatus)AUGraphRemoveRenderNotify (Handle, renderCallback, GCHandle.ToIntPtr (gcHandle));
#endif

graphUserCallbacks.Remove (callback); // Remove from list even if there is an error
return error;
}

HashSet<RenderDelegate> graphUserCallbacks = new HashSet<RenderDelegate> ();

#if !NET
static CallbackShared? _static_CallbackShared;
static CallbackShared static_CallbackShared {
get {
if (_static_CallbackShared is null)
_static_CallbackShared = new CallbackShared (renderCallback);
return _static_CallbackShared;
}
}
#endif

#if NET
[UnmanagedCallersOnly]
static unsafe AudioUnitStatus renderCallback(IntPtr inRefCon,
AudioUnitRenderActionFlags* _ioActionFlags,
AudioTimeStamp* _inTimeStamp,
uint _inBusNumber,
uint _inNumberFrames,
IntPtr _ioData)
#else
[MonoPInvokeCallback (typeof(CallbackShared))]
static AudioUnitStatus renderCallback(IntPtr inRefCon,
ref AudioUnitRenderActionFlags _ioActionFlags,
ref AudioTimeStamp _inTimeStamp,
uint _inBusNumber,
uint _inNumberFrames,
IntPtr _ioData)
#endif
{
// getting audiounit instance
var handler = GCHandle.FromIntPtr (inRefCon);
Expand All @@ -183,8 +218,13 @@ static AudioUnitStatus renderCallback(IntPtr inRefCon,

if (renderers.Count != 0) {
using (var buffers = new AudioBuffers (_ioData)) {
foreach (RenderDelegate renderer in renderers)
foreach (RenderDelegate renderer in renderers) {
#if NET
renderer (*_ioActionFlags, *_inTimeStamp, _inBusNumber, _inNumberFrames, buffers);
#else
renderer (_ioActionFlags, _inTimeStamp, _inBusNumber, _inNumberFrames, buffers);
#endif
}
return AudioUnitStatus.OK;
}
}
Expand Down Expand Up @@ -313,7 +353,9 @@ public AUGraphError DisconnectNodeInput (int destNode, uint destInputNumber)
}

Dictionary<uint, RenderDelegate>? nodesCallbacks;
#if !NET
static readonly CallbackShared CreateRenderCallback = RenderCallbackImpl;
#endif

public AUGraphError SetNodeInputCallback (int destNode, uint destInputNumber, RenderDelegate renderDelegate)
{
Expand All @@ -323,25 +365,39 @@ public AUGraphError SetNodeInputCallback (int destNode, uint destInputNumber, Re
nodesCallbacks [destInputNumber] = renderDelegate;

var cb = new AURenderCallbackStruct ();
#if NET
unsafe {
cb.Proc = &RenderCallbackImpl;
}
#else
cb.Proc = CreateRenderCallback;
#endif
cb.ProcRefCon = GCHandle.ToIntPtr (gcHandle);
return AUGraphSetNodeInputCallback (Handle, destNode, destInputNumber, ref cb);
}
#if NET
[UnmanagedCallersOnly]
static unsafe AudioUnitStatus RenderCallbackImpl (IntPtr clientData, AudioUnitRenderActionFlags* actionFlags, AudioTimeStamp* timeStamp, uint busNumber, uint numberFrames, IntPtr data)
#else

[MonoPInvokeCallback (typeof (CallbackShared))]
static AudioUnitStatus RenderCallbackImpl (IntPtr clientData, ref AudioUnitRenderActionFlags actionFlags, ref AudioTimeStamp timeStamp, uint busNumber, uint numberFrames, IntPtr data)
#endif
{
GCHandle gch = GCHandle.FromIntPtr (clientData);
var au = gch.Target as AUGraph;

if (au?.nodesCallbacks is null)
return AudioUnitStatus.InvalidParameter;

if (!au.nodesCallbacks.TryGetValue (busNumber, out var callback))
return AudioUnitStatus.InvalidParameter;

using (var buffers = new AudioBuffers (data)) {
#if NET
return callback (*actionFlags, *timeStamp, busNumber, numberFrames, buffers);
#else
return callback (actionFlags, timeStamp, busNumber, numberFrames, buffers);
#endif
}
}

Expand Down Expand Up @@ -435,10 +491,19 @@ protected override void Dispose (bool disposing)
static extern AUGraphError AUGraphInitialize (IntPtr inGraph);

[DllImport (Constants.AudioToolboxLibrary)]
#if NET
static unsafe extern int AUGraphAddRenderNotify (IntPtr inGraph, delegate* unmanaged<IntPtr, AudioUnitRenderActionFlags*, AudioTimeStamp*, uint, uint, IntPtr, AudioUnitStatus> inCallback, IntPtr inRefCon );
#else
static extern int AUGraphAddRenderNotify (IntPtr inGraph, CallbackShared inCallback, IntPtr inRefCon );
#endif

#if NET
[DllImport (Constants.AudioToolboxLibrary)]
static unsafe extern int AUGraphRemoveRenderNotify (IntPtr inGraph, delegate* unmanaged<IntPtr, AudioUnitRenderActionFlags*, AudioTimeStamp*, uint, uint, IntPtr, AudioUnitStatus> inCallback, IntPtr inRefCon );
#else
[DllImport (Constants.AudioToolboxLibrary)]
static extern int AUGraphRemoveRenderNotify (IntPtr inGraph, CallbackShared inCallback, IntPtr inRefCon );
#endif

[DllImport (Constants.AudioToolboxLibrary)]
static extern AUGraphError AUGraphStart (IntPtr inGraph);
Expand Down
52 changes: 49 additions & 3 deletions src/AudioUnit/AudioUnit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,25 @@ internal AudioUnitException (int k) : base (Lookup (k))
delegate AudioUnitStatus CallbackShared (IntPtr /* void* */ clientData, ref AudioUnitRenderActionFlags /* AudioUnitRenderActionFlags* */ actionFlags, ref AudioTimeStamp /* AudioTimeStamp* */ timeStamp, uint /* UInt32 */ busNumber, uint /* UInt32 */ numberFrames, IntPtr /* AudioBufferList* */ data);
#endif // !COREBUILD

#if NET
[StructLayout (LayoutKind.Sequential)]
unsafe struct AURenderCallbackStruct
{
#if COREBUILD
public delegate* unmanaged<IntPtr, int*, AudioTimeStamp*, uint, uint, IntPtr, int> Proc;
#else
public delegate* unmanaged<IntPtr, AudioUnitRenderActionFlags*, AudioTimeStamp*, uint, uint, IntPtr, AudioUnitStatus> Proc;
#endif
public IntPtr ProcRefCon;
}
#else
[StructLayout (LayoutKind.Sequential)]
struct AURenderCallbackStruct
{
public Delegate Proc;
public IntPtr ProcRefCon;
}
#endif

[StructLayout (LayoutKind.Sequential)]
struct AudioUnitConnection
Expand Down Expand Up @@ -349,8 +362,10 @@ public struct ImmediateStruct
public class AudioUnit : DisposableObject
{
#if !COREBUILD
#if !NET
static readonly CallbackShared CreateRenderCallback = RenderCallbackImpl;
static readonly CallbackShared CreateInputCallback = InputCallbackImpl;
#endif

GCHandle gcHandle;
bool _isPlaying;
Expand Down Expand Up @@ -657,26 +672,42 @@ public AudioUnitStatus SetRenderCallback (RenderDelegate renderDelegate, AudioUn
gcHandle = GCHandle.Alloc (this);

var cb = new AURenderCallbackStruct ();
#if NET
unsafe {
cb.Proc = &RenderCallbackImpl;
}
#else
cb.Proc = CreateRenderCallback;
#endif
cb.ProcRefCon = GCHandle.ToIntPtr (gcHandle);
return AudioUnitSetProperty (Handle, AudioUnitPropertyIDType.SetRenderCallback, scope, audioUnitElement, ref cb, Marshal.SizeOf (cb));
}

#if NET
[UnmanagedCallersOnly]
static unsafe AudioUnitStatus RenderCallbackImpl (IntPtr clientData, AudioUnitRenderActionFlags* actionFlags, AudioTimeStamp* timeStamp, uint busNumber, uint numberFrames, IntPtr data)
#else
[MonoPInvokeCallback (typeof (CallbackShared))]
static AudioUnitStatus RenderCallbackImpl (IntPtr clientData, ref AudioUnitRenderActionFlags actionFlags, ref AudioTimeStamp timeStamp, uint busNumber, uint numberFrames, IntPtr data)
#endif
{
GCHandle gch = GCHandle.FromIntPtr (clientData);
var au = (AudioUnit?) gch.Target;
var renderer = au?.renderer;

if (renderer is null)
return AudioUnitStatus.Uninitialized;

if (!renderer.TryGetValue (busNumber, out var render))
return AudioUnitStatus.Uninitialized;

using (var buffers = new AudioBuffers (data)) {
#if NET
unsafe {
return render (*actionFlags, *timeStamp, busNumber, numberFrames, buffers);
}
#else
return render (actionFlags, timeStamp, busNumber, numberFrames, buffers);
#endif
}
}

Expand All @@ -695,13 +726,23 @@ public AudioUnitStatus SetInputCallback (InputDelegate inputDelegate, AudioUnitS
gcHandle = GCHandle.Alloc (this);

var cb = new AURenderCallbackStruct ();
#if NET
unsafe {
cb.Proc = &InputCallbackImpl;
}
#else
cb.Proc = CreateInputCallback;
#endif
cb.ProcRefCon = GCHandle.ToIntPtr (gcHandle);
return AudioUnitSetProperty (Handle, AudioUnitPropertyIDType.SetInputCallback, scope, audioUnitElement, ref cb, Marshal.SizeOf (cb));
}

#if NET
[UnmanagedCallersOnly]
static unsafe AudioUnitStatus InputCallbackImpl (IntPtr clientData, AudioUnitRenderActionFlags* actionFlags, AudioTimeStamp* timeStamp, uint busNumber, uint numberFrames, IntPtr data)
#else
[MonoPInvokeCallback (typeof (CallbackShared))]
static AudioUnitStatus InputCallbackImpl (IntPtr clientData, ref AudioUnitRenderActionFlags actionFlags, ref AudioTimeStamp timeStamp, uint busNumber, uint numberFrames, IntPtr data)
#endif
{
GCHandle gch = GCHandle.FromIntPtr (clientData);
var au = gch.Target as AudioUnit;
Expand All @@ -714,8 +755,13 @@ static AudioUnitStatus InputCallbackImpl (IntPtr clientData, ref AudioUnitRender

if (!inputs.TryGetValue (busNumber, out var input))
return AudioUnitStatus.Uninitialized;

#if NET
unsafe {
return input (*actionFlags, *timeStamp, busNumber, numberFrames, au);
}
#else
return input (actionFlags, timeStamp, busNumber, numberFrames, au);
#endif
}

#endregion
Expand Down

6 comments on commit 1efe5ff

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

@vs-mobiletools-engineering-service2

This comment was marked as outdated.

Please sign in to comment.