Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[.NET/AudioUnit] Use [UnmanagedCallersOnly] instead of [MonoPInvokeCallback] Partial Fix for #10470 #15808

Merged
merged 6 commits into from
Sep 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
rolfbjarne marked this conversation as resolved.
Show resolved Hide resolved
#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