diff --git a/src/AudioUnit/AUGraph.cs b/src/AudioUnit/AUGraph.cs index 797aa5219a57..40451db0bdf7 100644 --- a/src/AudioUnit/AUGraph.cs +++ b/src/AudioUnit/AUGraph.cs @@ -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); @@ -156,8 +163,15 @@ 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; @@ -165,6 +179,26 @@ public AudioUnitStatus RemoveRenderNotify (RenderDelegate callback) HashSet graphUserCallbacks = new HashSet (); +#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, @@ -172,6 +206,7 @@ static AudioUnitStatus renderCallback(IntPtr inRefCon, uint _inBusNumber, uint _inNumberFrames, IntPtr _ioData) +#endif { // getting audiounit instance var handler = GCHandle.FromIntPtr (inRefCon); @@ -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; } } @@ -313,7 +353,9 @@ public AUGraphError DisconnectNodeInput (int destNode, uint destInputNumber) } Dictionary? nodesCallbacks; +#if !NET static readonly CallbackShared CreateRenderCallback = RenderCallbackImpl; +#endif public AUGraphError SetNodeInputCallback (int destNode, uint destInputNumber, RenderDelegate renderDelegate) { @@ -323,17 +365,27 @@ 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; @@ -341,7 +393,11 @@ static AudioUnitStatus RenderCallbackImpl (IntPtr clientData, ref AudioUnitRende 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 } } @@ -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 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 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); diff --git a/src/AudioUnit/AudioUnit.cs b/src/AudioUnit/AudioUnit.cs index 36ef16237c4b..6811c3e90780 100644 --- a/src/AudioUnit/AudioUnit.cs +++ b/src/AudioUnit/AudioUnit.cs @@ -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 Proc; +#else + public delegate* unmanaged Proc; +#endif + public IntPtr ProcRefCon; + } +#else [StructLayout (LayoutKind.Sequential)] struct AURenderCallbackStruct { public Delegate Proc; public IntPtr ProcRefCon; } +#endif [StructLayout (LayoutKind.Sequential)] struct AudioUnitConnection @@ -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; @@ -657,18 +672,28 @@ 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; @@ -676,7 +701,13 @@ static AudioUnitStatus RenderCallbackImpl (IntPtr clientData, ref AudioUnitRende 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 } } @@ -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; @@ -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