// %BANNER_BEGIN% // --------------------------------------------------------------------- // %COPYRIGHT_BEGIN% // Copyright (c) (2018-2022) Magic Leap, Inc. All Rights Reserved. // Use of this file is governed by the Software License Agreement, located here: https://www.magicleap.com/software-license-agreement-ml2 // Terms and conditions applicable to third-party materials accompanying this distribution may also be found in the top-level NOTICE file appearing herein. // %COPYRIGHT_END% // --------------------------------------------------------------------- // %BANNER_END% namespace UnityEngine.XR.MagicLeap { using System; using System.Runtime.InteropServices; using UnityEngine.XR.MagicLeap.Native; /// /// MLWebRTC class contains the API to interface with the /// WebRTC C API. /// public partial class MLWebRTC { /// /// Class that represents an audio sink used by the MLWebRTC API. /// public partial class AudioSink { /// /// Native bindings for the MLWebRTC.AudioSink class. /// internal class NativeBindings : MagicLeapNativeBindings { /// /// Creates an audio sink. /// /// The handle to the audio sink to return to the caller. /// /// MLResult.Result will be MLResult.Code.Ok if the audio sink was successfully created. /// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing. /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkCreate(out ulong sinkHandle); /// /// Creates an audio sink with the provided params. /// /// Parameters to use to create the audio sink. /// The handle to the audio sink to return to the caller. /// /// MLResult.Result will be MLResult.Code.Ok if the audio sink was successfully created. /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkCreateEx([In] ref MLWebRTCAudioSinkParams sinkParams, out ulong sinkHandle); /// /// Sets the source of an audio sink. /// /// The handle to the audio sink. /// The handle to the source to set onto the audio sink. /// /// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful. /// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was sent. /// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing. /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkSetSource(ulong sinkHandle, ulong sourceHandle); /// /// Sets the world position of an audio sink for spatialized audio. /// /// The handle to the audio sink. /// The position to set the audio sink to. /// /// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful. /// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was sent. /// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing. /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkSetPosition(ulong sinkHandle, in MagicLeapNativeBindings.MLVec3f position); /// /// Sets the world position of an audio sink channel. /// /// The handle to the audio sink. /// The position & channel to set the audio sink spatialization for. /// /// MLResult.Result will be MLResult.Code.Ok if spatialization settings were set successfully. /// MLResult.Result will be MLResult.Code.WebRTCResultInstanceNotCreated if MLWebRTC instance was not created. /// MLResult.Result will be MLResult.Code.WebRTCResultMismatchingHandle if an incorrect handle was sent. /// MLResult.Result will be MLResult.Code.InvalidParam if an incorrect param was sent. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkSetPositionEx(ulong sinkHandle, [In] ref MLWebRTCAudioSinkPosition position); /// /// Sets the volume of an audio sink. /// /// The handle to the audio sink. /// The sink volume. /// /// MLResult.Result will be MLResult.Code.Ok if the volume was set successfully. /// MLResult.Result will be MLResult.Code.WebRTCResultInstanceNotCreated if MLWebRTC instance was not created. /// MLResult.Result will be MLResult.Code.WebRTCResultMismatchingHandle if an incorrect handle was sent. /// MLResult.Result will be MLResult.Code.InvalidParam if an incorrect param was sent. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkSetSoundVolume(ulong sinkHandle, float volume); /// /// Sets the world orientation of an audio sink channel. /// /// The handle to the audio sink. /// The orientation & channel to set the audio sink spatialization for. /// /// MLResult.Result will be MLResult.Code.Ok if spatialization settings were set successfully. /// MLResult.Result will be MLResult.Code.WebRTCResultInstanceNotCreated if MLWebRTC instance was not created. /// MLResult.Result will be MLResult.Code.WebRTCResultMismatchingHandle if an incorrect handle was sent. /// MLResult.Result will be MLResult.Code.InvalidParam if an incorrect param was sent. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkSetSoundOrientation(ulong sinkHandle, [In] ref MLWebRTCAudioSinkOrientation orientation); /// /// Sets the direct spatial sound send levels of an audio sink channel. /// /// The handle to the audio sink. /// The sound levels & channel to set the audio sink spatialization for. /// /// MLResult.Result will be MLResult.Code.Ok if spatialization settings were set successfully. /// MLResult.Result will be MLResult.Code.WebRTCResultInstanceNotCreated if MLWebRTC instance was not created. /// MLResult.Result will be MLResult.Code.WebRTCResultMismatchingHandle if an incorrect handle was sent. /// MLResult.Result will be MLResult.Code.InvalidParam if an incorrect param was sent. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkSetDirectSoundLevels(ulong sinkHandle, [In] ref MLWebRTCAudioSinkSoundLevels soundLevels); /// /// Sets the room spatial sound send levels of an audio sink channel. /// /// The handle to the audio sink. /// The sound levels & channel to set the audio sink spatialization for. /// /// MLResult.Result will be MLResult.Code.Ok if spatialization settings were set successfully. /// MLResult.Result will be MLResult.Code.WebRTCResultInstanceNotCreated if MLWebRTC instance was not created. /// MLResult.Result will be MLResult.Code.WebRTCResultMismatchingHandle if an incorrect handle was sent. /// MLResult.Result will be MLResult.Code.InvalidParam if an incorrect param was sent. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkSetRoomSoundLevels(ulong sinkHandle, [In] ref MLWebRTCAudioSinkSoundLevels soundLevels); /// /// Sets the distance properties of an audio sink channel. /// /// The handle to the audio sink. /// The sound distance & channel to set the audio sink spatialization for. /// /// MLResult.Result will be MLResult.Code.Ok if spatialization settings were set successfully. /// MLResult.Result will be MLResult.Code.WebRTCResultInstanceNotCreated if MLWebRTC instance was not created. /// MLResult.Result will be MLResult.Code.WebRTCResultMismatchingHandle if an incorrect handle was sent. /// MLResult.Result will be MLResult.Code.InvalidParam if an incorrect param was sent. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkSetSoundDistanceProperties(ulong sinkHandle, [In] ref MLWebRTCAudioSinkSoundDistance soundDistance); /// /// Sets the radiation properties of an audio sink channel. /// /// The handle to the audio sink. /// The sound radiation & channel to set the audio sink spatialization for. /// /// MLResult.Result will be MLResult.Code.Ok if spatialization settings were set successfully. /// MLResult.Result will be MLResult.Code.WebRTCResultInstanceNotCreated if MLWebRTC instance was not created. /// MLResult.Result will be MLResult.Code.WebRTCResultMismatchingHandle if an incorrect handle was sent. /// MLResult.Result will be MLResult.Code.InvalidParam if an incorrect param was sent. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkSetSoundRadiationProperties(ulong sinkHandle, [In] ref MLWebRTCAudioSinkSoundRadiation soundRadiation); /// /// Resets the world position of an audio sink for spatialized audio. /// /// The handle to the audio sink. /// /// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful. /// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was sent. /// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing. /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkResetPosition(ulong sinkHandle); /// /// Sets the number of milliseconds of audio that should be cached in the buffers before dropping the packets. /// Dictates the audio latency when app recovers from lifecycle state transitions like standby & reality. /// Default is 200ms. /// /// The handle to the audio sink. /// How many milliseconds worth of audio to cache. /// /// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful. /// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was sent. /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkSetCacheSize(ulong sinkHandle, uint millisecondsToCache); /// /// Destroys an audio sink. /// /// The handle to the audio sink. /// /// MLResult.Result will be MLResult.Code.Ok if destroying all handles was successful. /// MLResult.Result will be MLResult.Code.MismatchingHandle if an incorrect handle was sent. /// MLResult.Result will be MLResult.Code.PermissionDenied if necessary permission is missing. /// MLResult.Result will be MLResult.Code.UnspecifiedFailure if failed due to other internal error. /// [DllImport(MLWebRTCDLL, CallingConvention = CallingConvention.Cdecl)] public static extern MLResult.Code MLWebRTCAudioSinkDestroy(ulong sinkHandle); private delegate void OnAudioSinkDataAvailableDelegate(in MLAudioOutput.NativeBindings.MLAudioBuffer audioBuffer, in MLAudioOutput.NativeBindings.MLAudioBufferFormat audioBufferFormat, IntPtr userContext); [AOT.MonoPInvokeCallback(typeof(OnAudioSinkDataAvailableDelegate))] private static void OnAudioSinkDataAvailable(in MLAudioOutput.NativeBindings.MLAudioBuffer audioBuffer, in MLAudioOutput.NativeBindings.MLAudioBufferFormat audioBufferFormat, IntPtr userContext) { GCHandle gcHandle = GCHandle.FromIntPtr(userContext); AudioSink audioSink = gcHandle.Target as AudioSink; if (audioSink == null) { return; } MLAudioOutput.Buffer buffer = new MLAudioOutput.Buffer(audioBuffer, audioBufferFormat, audioSink.CopyRawAudioDataToManagedMemory || (audioSink.OnAudioDataAvailable != null)); if (audioSink.OnAudioDataAvailable_NativeCallbackThread != null) { audioSink.OnAudioDataAvailable_NativeCallbackThread.Invoke(buffer); } MLThreadDispatch.ScheduleMain(() => { audioSink.OnAudioDataAvailable?.Invoke(buffer); }); } private delegate void OnAudioServiceStatusDelegate(ServiceStatus status, IntPtr userContext); [AOT.MonoPInvokeCallback(typeof(OnAudioServiceStatusChangedDelegate))] private static void OnAudioServiceStatus(ServiceStatus status, IntPtr userContext) { GCHandle gcHandle = GCHandle.FromIntPtr(userContext); AudioSink audioSink = gcHandle.Target as AudioSink; if (audioSink == null) { return; } MLThreadDispatch.ScheduleMain(() => { audioSink.CurrentServiceStatus = status; audioSink.OnAudioServiceStatusChanged?.Invoke(status); }); } [StructLayout(LayoutKind.Sequential)] public struct MLWebRTCAudioSinkParams { private readonly uint version; private readonly AudioSink.BufferNotifyMode mode; private readonly IntPtr userContext; private readonly OnAudioSinkDataAvailableDelegate audioSinkCallback; private readonly OnAudioServiceStatusDelegate audioServiceStatusCallback; public MLWebRTCAudioSinkParams(AudioSink audioSink) { this.version = 1; this.mode = audioSink.Mode; if (audioSink.Mode != BufferNotifyMode.None) { this.audioSinkCallback = OnAudioSinkDataAvailable; } else { this.audioSinkCallback = null; } this.audioServiceStatusCallback = OnAudioServiceStatus; this.userContext = GCHandle.ToIntPtr(audioSink.gcHandle); } } [StructLayout(LayoutKind.Sequential)] public struct MLWebRTCAudioSinkPosition { /// /// Struct version /// private uint version; /// /// Ptr to position (MLVec3f) of the audio channel /// private IntPtr positionPtr; /// /// Channel to set this position for. Set to -1 to set for all avaialble channels. /// private int channel; public void Update(Vector3 position, int channel = -1) { if (this.positionPtr == IntPtr.Zero) { this.version = 1; this.positionPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MagicLeapNativeBindings.MLVec3f))); } Marshal.StructureToPtr(MLConvert.FromUnity(position), this.positionPtr, false); this.channel = channel; } public void FreeUnmanagedMemory() { if (positionPtr != IntPtr.Zero) { Marshal.FreeHGlobal(positionPtr); positionPtr = IntPtr.Zero; } } } [StructLayout(LayoutKind.Sequential)] public struct MLWebRTCAudioSinkOrientation { /// /// Struct version /// private uint version; /// /// Ptr to orientation (MLQuatf) of the audio channel /// private IntPtr orientationPtr; /// /// Channel to set this position for. Set to -1 to set for all avaialble channels. /// private int channel; public void Update(Quaternion orientation, int channel = -1) { if (this.orientationPtr == IntPtr.Zero) { this.version = 1; this.orientationPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MagicLeapNativeBindings.MLQuaternionf))); } Marshal.StructureToPtr(MLConvert.FromUnity(orientation), this.orientationPtr, false); this.channel = channel; } public void FreeUnmanagedMemory() { if (orientationPtr != IntPtr.Zero) { Marshal.FreeHGlobal(orientationPtr); orientationPtr = IntPtr.Zero; } } } [StructLayout(LayoutKind.Sequential)] public struct MLWebRTCAudioSinkSoundLevels { private uint version; private IntPtr sendLevelsPtr; private int channel; public void Update(MLAudioOutput.SpatialSound.SendLevels sendLevels, int channel = -1) { if (this.sendLevelsPtr == IntPtr.Zero) { this.version = 1; this.sendLevelsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MLAudioOutput.NativeBindings.MLAudioSpatialSoundSendLevels))); } Marshal.StructureToPtr(new MLAudioOutput.NativeBindings.MLAudioSpatialSoundSendLevels(sendLevels), this.sendLevelsPtr, false); this.channel = channel; } public void FreeUnmanagedMemory() { if (sendLevelsPtr != IntPtr.Zero) { Marshal.FreeHGlobal(sendLevelsPtr); sendLevelsPtr = IntPtr.Zero; } } } [StructLayout(LayoutKind.Sequential)] public struct MLWebRTCAudioSinkSoundDistance { private uint version; private IntPtr distancePropertiesPtr; private int channel; public void Update(MLAudioOutput.SpatialSound.DistanceProperties distanceProperties, int channel = -1) { if (this.distancePropertiesPtr == IntPtr.Zero) { this.version = 1; this.distancePropertiesPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MLAudioOutput.NativeBindings.MLAudioSpatialSoundDistanceProperties))); } Marshal.StructureToPtr(new MLAudioOutput.NativeBindings.MLAudioSpatialSoundDistanceProperties(distanceProperties), this.distancePropertiesPtr, false); this.channel = channel; } public void FreeUnmanagedMemory() { if (distancePropertiesPtr != IntPtr.Zero) { Marshal.FreeHGlobal(distancePropertiesPtr); distancePropertiesPtr = IntPtr.Zero; } } } [StructLayout(LayoutKind.Sequential)] public struct MLWebRTCAudioSinkSoundRadiation { private uint version; private IntPtr radiationPropertiesPtr; private int channel; public void Update(MLAudioOutput.SpatialSound.RadiationProperties radiationProperties, int channel = -1) { if (this.radiationPropertiesPtr == IntPtr.Zero) { this.version = 1; this.radiationPropertiesPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MLAudioOutput.NativeBindings.MLAudioSpatialSoundRadiationProperties))); } Marshal.StructureToPtr(new MLAudioOutput.NativeBindings.MLAudioSpatialSoundRadiationProperties(radiationProperties), this.radiationPropertiesPtr, false); this.channel = channel; } public void FreeUnmanagedMemory() { if (radiationPropertiesPtr != IntPtr.Zero) { Marshal.FreeHGlobal(radiationPropertiesPtr); radiationPropertiesPtr = IntPtr.Zero; } } } } } } }