// %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;
}
}
}
}
}
}
}