// %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% using System; using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine; namespace UnityEngine.XR.MagicLeap { public sealed partial class MLAudioOutput { /// /// Possible sample formats for input and output streams. /// public enum SampleFormatType : uint { Int, Float } /// /// Possible channel formats for input and output streams. /// public enum ChannelFormatType : uint { Default, AmbisonicAmbix } /// /// Buffer format settings for input and output streams. /// public struct BufferFormat { /// /// Number of channels. /// public uint ChannelCount { get; set; } /// /// Sample rate. /// public uint SamplesPerSecond { get; set; } /// /// Number of bits per sample. /// public uint BitsPerSample { get; set; } /// /// Number of bits used per sample. /// public uint ValidBitsPerSample { get; set; } /// /// Integer or float. /// public SampleFormatType SampleFormat { get; set; } /// /// Channel format. /// public ChannelFormatType ChannelFormat { get; set; } internal BufferFormat(MLAudioOutput.NativeBindings.MLAudioBufferFormat bufferFormatNative) { this.ChannelCount = bufferFormatNative.ChannelCount; this.SamplesPerSecond = bufferFormatNative.SamplesPerSecond; this.BitsPerSample = bufferFormatNative.BitsPerSample; this.ValidBitsPerSample = bufferFormatNative.ValidBitsPerSample; this.SampleFormat = bufferFormatNative.SampleFormat; this.ChannelFormat = bufferFormatNative.ChannelFormat; } public BufferFormat(uint channelCount, uint samplesPerSecond, uint bitsPerSample, SampleFormatType sampleFormat = SampleFormatType.Float, ChannelFormatType channelFormat = ChannelFormatType.Default) { this.ChannelCount = channelCount; this.SamplesPerSecond = samplesPerSecond; this.BitsPerSample = bitsPerSample; this.ValidBitsPerSample = bitsPerSample; this.SampleFormat = sampleFormat; this.ChannelFormat = channelFormat; } public override string ToString() { return $"ChannelCount: {ChannelCount}, SamplesPerSec: {SamplesPerSecond}, BitsPerSample: {BitsPerSample}, ValidBitsPerSample: {ValidBitsPerSample}, SampleFormat: {SampleFormat}, ChannelFormat: {ChannelFormat}"; } } /// /// An audio buffer for passing data from input streams or to output streams. /// public struct Buffer { /// /// Pointer to audio data. Only valid when received on native callback threads. /// public IntPtr NativeDataPtr; /// /// Size of audio data in bytes. Only valid when received on native callback threads. /// public uint Size; /// /// Audio samples in this buffer. /// public float[] Samples; /// /// Audio buffer format. /// public BufferFormat Format; internal Buffer(MLAudioOutput.NativeBindings.MLAudioBuffer buffer, MLAudioOutput.NativeBindings.MLAudioBufferFormat bufferFormat, bool copyToManagedMemory = false) { this.NativeDataPtr = buffer.Ptr; this.Size = buffer.Size; this.Format = new BufferFormat(bufferFormat); this.Samples = null; if (copyToManagedMemory) { this.Samples = ConvertToManagedFloatSamples(Size, NativeDataPtr, Format); } } private static readonly Dictionary> UnmanagedToFloat = new Dictionary>() { { 8, (ptr) => Marshal.ReadByte (ptr) / (float)byte.MaxValue }, { 16, (ptr) => Marshal.ReadInt16(ptr) / (float)short.MaxValue }, { 32, (ptr) => Marshal.ReadInt32(ptr) / (float)int.MaxValue }, { 64, (ptr) => Marshal.ReadInt64(ptr) / (float)long.MaxValue } }; /// /// Copy the provided unmanaged audio buffer to managed memory and convert the samples to float. /// /// Number of bytes allocated for the unmanaged buffer /// Pointer to the unmanaged buffer /// Audio format for the unmanaged buffer /// Array of managed memory containing float samples public static float[] ConvertToManagedFloatSamples(uint size, IntPtr bufferPtr, BufferFormat format) { uint bytesPerSample = format.BitsPerSample / 8; uint numSamples = size / bytesPerSample; float[] samples = new float[numSamples]; if (format.SampleFormat == SampleFormatType.Float) { Marshal.Copy(bufferPtr, samples, 0, (int)numSamples); } else if (format.SampleFormat == SampleFormatType.Int) { var toFloat = UnmanagedToFloat[format.BitsPerSample]; for (uint i = 0; i < numSamples; ++i) { samples[i] = toFloat(bufferPtr); bufferPtr = IntPtr.Add(bufferPtr, (int)bytesPerSample); } } return samples; } public Buffer(BufferFormat format, float[] samples) { this.NativeDataPtr = IntPtr.Zero; this.Size = 0; this.Samples = samples; this.Format = format; } } } }