223 lines
5.7 KiB
C#
223 lines
5.7 KiB
C#
using UnityEngine;
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
#if UNITY_IOS || UNITY_TVOS || ENABLE_IL2CPP
|
|
using AOT;
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright 2012-2021 RenderHeads Ltd. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace RenderHeads.Media.AVProMovieCapture
|
|
{
|
|
public enum AmbisonicOrder : int
|
|
{
|
|
//Zero = 0,
|
|
First = 1,
|
|
Second = 2,
|
|
Third = 3,
|
|
}
|
|
|
|
public enum AmbisonicFormat
|
|
{
|
|
FuMa, // FuMa channel ordering and normalisation
|
|
ACN_SN3D, // ACN channel ordering with SN3D normalisation
|
|
}
|
|
|
|
public enum AmbisonicChannelOrder : int
|
|
{
|
|
FuMa,
|
|
ACN,
|
|
}
|
|
|
|
public enum AmbisonicNormalisation : int
|
|
{
|
|
FuMa,
|
|
SN3D,
|
|
}
|
|
|
|
public partial class NativePlugin
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Ambisonic
|
|
|
|
[DllImport(PluginName)]
|
|
public static extern IntPtr AddAmbisonicSourceInstance(int maxCoefficients);
|
|
|
|
[DllImport(PluginName)]
|
|
public static extern void RemoveAmbisonicSourceInstance(IntPtr instance);
|
|
|
|
[DllImport(PluginName)]
|
|
public static extern void UpdateAmbisonicWeights(IntPtr instance, float azimuth, float elevation, AmbisonicOrder order, AmbisonicChannelOrder channelOrder, float[] normalisationWeights);
|
|
|
|
[DllImport(PluginName)]
|
|
public static extern void EncodeMonoToAmbisonic(IntPtr instance, float[] inSamples, int inSamplesOffset, int inFrameCount, int inChannelCount, float[] outSamples, int outSamplesOffset, int outSamplesLength, AmbisonicOrder order);
|
|
}
|
|
|
|
public static class Ambisonic
|
|
{
|
|
public const int MaxCoeffs = 16;
|
|
static float[] _weightsFuMa = null;
|
|
static float[] _weightsSN3D = null;
|
|
|
|
public static float[] GetNormalisationWeights(AmbisonicNormalisation normalisation)
|
|
{
|
|
return (normalisation == AmbisonicNormalisation.FuMa) ? _weightsFuMa : _weightsSN3D;
|
|
}
|
|
|
|
public static int GetCoeffCount(AmbisonicOrder order)
|
|
{
|
|
if (order == AmbisonicOrder.First) { return 4; }
|
|
else if (order == AmbisonicOrder.Second) { return 9; }
|
|
else if (order == AmbisonicOrder.Third) { return 16; }
|
|
return 0;
|
|
}
|
|
|
|
public static AmbisonicChannelOrder GetChannelOrder(AmbisonicFormat format)
|
|
{
|
|
return (format == AmbisonicFormat.FuMa) ? AmbisonicChannelOrder.FuMa : AmbisonicChannelOrder.ACN;
|
|
}
|
|
|
|
public static AmbisonicNormalisation GetNormalisation(AmbisonicFormat format)
|
|
{
|
|
return (format == AmbisonicFormat.FuMa) ? AmbisonicNormalisation.FuMa : AmbisonicNormalisation.SN3D;
|
|
}
|
|
|
|
static Ambisonic()
|
|
{
|
|
_weightsFuMa = BuildWeightsFuMa();
|
|
_weightsSN3D = BuildWeightsSN3D();
|
|
}
|
|
|
|
static float[] BuildWeightsFuMa()
|
|
{
|
|
float[] w = new float[MaxCoeffs];
|
|
w[0] = 1f / Mathf.Sqrt(2f);
|
|
|
|
w[1] = 1f;
|
|
w[2] = 1f;
|
|
w[3] = 1f;
|
|
|
|
w[4] = 1f;
|
|
w[5] = 2f / Mathf.Sqrt(3f);
|
|
w[6] = 2f / Mathf.Sqrt(3f);
|
|
w[7] = 2f / Mathf.Sqrt(3f);
|
|
w[8] = 2f / Mathf.Sqrt(3f);
|
|
|
|
w[9] = 1f;
|
|
w[10] = Mathf.Sqrt(45f / 32f);
|
|
w[11] = Mathf.Sqrt(45f / 32f);
|
|
w[12] = 3f / Mathf.Sqrt(5f);
|
|
w[13] = 3f / Mathf.Sqrt(5f);
|
|
w[14] = Mathf.Sqrt(8f / 5f);
|
|
w[15] = Mathf.Sqrt(8f / 5f);
|
|
|
|
return w;
|
|
}
|
|
|
|
// Returns N which is the same as the order
|
|
static int GetN(int acn)
|
|
{
|
|
return Mathf.FloorToInt(Mathf.Sqrt(acn));
|
|
}
|
|
|
|
// Returns M which is the signed delta offset from the middle of the pyramid
|
|
static int GetM(int acn)
|
|
{
|
|
int n = GetN(acn);
|
|
return acn - (n * n) - n;
|
|
}
|
|
|
|
static int Factorial(int x)
|
|
{
|
|
int result = 1;
|
|
for (int i = 2; i <= x; i++)
|
|
{
|
|
result *= i;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static float GetNormalisationSN3D(int acn)
|
|
{
|
|
int n = GetN(acn);
|
|
int m = GetM(acn);
|
|
return GetNormalisationSN3D(n, m);
|
|
}
|
|
|
|
static float GetNormalisationSN3D(int n, int m)
|
|
{
|
|
float dm = (m == 0) ? 1f : 0f;
|
|
float l1 = (2f - dm);
|
|
|
|
float a = Factorial(n - Mathf.Abs(m));
|
|
float b = Factorial(n + Mathf.Abs(m));
|
|
float l2 = a / b;
|
|
|
|
return Mathf.Sqrt(l1 * l2);
|
|
}
|
|
|
|
static float GetNormalisationN3D(int n, int m)
|
|
{
|
|
return GetNormalisationSN3D(n, m) * Mathf.Sqrt(2f * n + 1f);
|
|
}
|
|
|
|
static float[] BuildWeightsSN3D()
|
|
{
|
|
float[] w = new float[MaxCoeffs];
|
|
for (int acn = 0; acn < w.Length; acn++)
|
|
{
|
|
w[acn] = GetNormalisationSN3D(acn);
|
|
}
|
|
return w;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The coordinate system used in Ambisonics follows the right hand rule convention with
|
|
/// positive X pointing forwards,
|
|
/// positive Y pointing to the left and
|
|
/// positive Z pointing upwards
|
|
/// Horizontal angles run anticlockwise from due front and
|
|
/// vertical angles are positive above the horizontal, negative below.
|
|
/// </summary>
|
|
internal struct PolarCoord
|
|
{
|
|
/// Azimuth (horizontal) angle in radians, 0..2PI
|
|
public float azimuth;
|
|
|
|
/// Elevation (vertical) angle in radians, -PI..PI
|
|
public float elevation;
|
|
|
|
//public float distance;
|
|
|
|
public void FromCart(Vector3 position)
|
|
{
|
|
// Convert from Unity's left-hand system to Ambisonics right-hand system
|
|
float x = position.z;
|
|
float y = -position.x;
|
|
float z = position.y;
|
|
|
|
// The azimuth angle is zero straight ahead and increases counter-clockwise.
|
|
azimuth = Mathf.Rad2Deg * Mathf.Atan2(y, x);
|
|
|
|
// Clamp
|
|
if (azimuth < 0f)
|
|
{
|
|
azimuth += 360f;
|
|
}
|
|
|
|
// The elevation angle is zero on the horizontal plane and positive in the upper hemisphere.
|
|
elevation = Mathf.Rad2Deg * Mathf.Atan2(z, Mathf.Sqrt(x * x + y * y));
|
|
elevation = Mathf.Clamp(elevation, -90f, 90f);
|
|
|
|
// NOTE: Distance is not currently used, but there may be scope in the future
|
|
//distance = Mathf.Sqrt( x * x + y * y + z * z );
|
|
|
|
// Back to radians
|
|
azimuth = Mathf.Deg2Rad * azimuth;
|
|
elevation = Mathf.Deg2Rad * elevation;
|
|
}
|
|
};
|
|
}
|
|
} |