Init
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 72b2687315f3a4375aceab7d9de97c79
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright © 2015-2021 Pico Technology Co., Ltd. All Rights Reserved.
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(AudioSource))]
|
||||
public class PXR_Audio_Spatializer_AmbisonicSource : MonoBehaviour
|
||||
{
|
||||
private AudioSource nativeSource;
|
||||
|
||||
private float playheadPosition = 0.0f;
|
||||
private bool wasPlaying = false;
|
||||
|
||||
/// <summary>
|
||||
/// Resume audio playing status.
|
||||
/// </summary>
|
||||
public void Resume()
|
||||
{
|
||||
if (nativeSource)
|
||||
{
|
||||
nativeSource.time = playheadPosition;
|
||||
if (wasPlaying)
|
||||
{
|
||||
nativeSource.Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
nativeSource = GetComponent<AudioSource>();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (nativeSource.isPlaying)
|
||||
playheadPosition = nativeSource.time;
|
||||
wasPlaying = nativeSource.isPlaying;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a36d16e4ff48444b8ac021c183d3e3fd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright © 2015-2022 Pico Technology Co., Ltd. All Rights Reserved.
|
||||
|
||||
using System.Collections;
|
||||
using System.Runtime.InteropServices;
|
||||
using PXR_Audio.Spatializer;
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(AudioListener))]
|
||||
public class PXR_Audio_Spatializer_AudioListener : MonoBehaviour
|
||||
{
|
||||
private enum OutputMethod
|
||||
{
|
||||
OnAudioFilterRead,
|
||||
PicoAudioRouter
|
||||
}
|
||||
|
||||
[Tooltip("Determine where the output spatial audio signal goes:\n" +
|
||||
" - On Audio Filter Read: Spatial audio signal got mixed with the rest of the game.\n" +
|
||||
" - Pico Audio Router: Spatial audio signal got transmitted to one or more 'Pico Audio Router' effects in the Unity Audio Mixer to gain more control to your mix.")]
|
||||
[SerializeField]
|
||||
private OutputMethod outputMethod = OutputMethod.OnAudioFilterRead;
|
||||
|
||||
private float[] temp_output_buffer = new float[2048];
|
||||
|
||||
private bool isActive;
|
||||
|
||||
private PXR_Audio_Spatializer_Context context;
|
||||
|
||||
private PXR_Audio_Spatializer_Context Context
|
||||
{
|
||||
get
|
||||
{
|
||||
if (context == null)
|
||||
context = PXR_Audio_Spatializer_Context.Instance;
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
private float[] positionArray = new float[3] { 0.0f, 0.0f, 0.0f };
|
||||
private float[] frontArray = new float[3] { 0.0f, 0.0f, 0.0f };
|
||||
private float[] upArray = new float[3] { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
private bool isAudioDSPInProgress = false;
|
||||
|
||||
public bool IsAudioDSPInProgress
|
||||
{
|
||||
get { return isAudioDSPInProgress; }
|
||||
}
|
||||
|
||||
internal void RegisterInternal()
|
||||
{
|
||||
// Initialize listener pose
|
||||
if (Context.spatializerApiImpl != SpatializerApiImpl.wwise)
|
||||
{
|
||||
UpdatePose();
|
||||
}
|
||||
|
||||
isActive = true;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
// Wait for context to be initialized
|
||||
if (Context != null && Context.Initialized)
|
||||
RegisterInternal();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (isActive && context != null && context.Initialized && transform.hasChanged &&
|
||||
context.spatializerApiImpl != SpatializerApiImpl.wwise)
|
||||
{
|
||||
UpdatePose();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
isActive = false;
|
||||
isAudioDSPInProgress = false;
|
||||
}
|
||||
|
||||
void UpdatePose()
|
||||
{
|
||||
positionArray[0] = transform.position.x;
|
||||
positionArray[1] = transform.position.y;
|
||||
positionArray[2] = -transform.position.z;
|
||||
frontArray[0] = transform.forward.x;
|
||||
frontArray[1] = transform.forward.y;
|
||||
frontArray[2] = -transform.forward.z;
|
||||
upArray[0] = transform.up.x;
|
||||
upArray[1] = transform.up.y;
|
||||
upArray[2] = -transform.up.z;
|
||||
Context.SetListenerPose(positionArray, frontArray, upArray);
|
||||
}
|
||||
|
||||
[DllImport("PicoAudioRouter", EntryPoint = "yggdrasil_audio_unity_audio_router_input")]
|
||||
private static extern void PicoAudioRouterInput(float[] inBuffer, int inBufferSize, int inChannels);
|
||||
|
||||
private void OnAudioFilterRead(float[] data, int channels)
|
||||
{
|
||||
if (!isActive || context == null || !context.Initialized ||
|
||||
Context.spatializerApiImpl == SpatializerApiImpl.wwise)
|
||||
return;
|
||||
|
||||
isAudioDSPInProgress = true;
|
||||
if (outputMethod == OutputMethod.OnAudioFilterRead)
|
||||
context.GetInterleavedBinauralBuffer(data, (uint)(data.Length / channels), true);
|
||||
else if (outputMethod == OutputMethod.PicoAudioRouter)
|
||||
{
|
||||
context.GetInterleavedBinauralBuffer(temp_output_buffer, (uint)(data.Length / channels), false);
|
||||
PicoAudioRouterInput(temp_output_buffer, data.Length / channels, channels);
|
||||
}
|
||||
|
||||
isAudioDSPInProgress = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67f4b0fc61d6d4b6499b28a0a4461b6f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,586 @@
|
||||
// Copyright © 2015-2022 Pico Technology Co., Ltd. All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using PXR_Audio.Spatializer;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(AudioSource))]
|
||||
public class PXR_Audio_Spatializer_AudioSource : MonoBehaviour
|
||||
{
|
||||
[SerializeField] [Range(0.0f, 24.0f)]
|
||||
private float sourceGainDB = 0.0f;
|
||||
|
||||
private float sourceGainAmplitude = 1.0f;
|
||||
|
||||
[SerializeField] [Range(-120.0f, 48.0f)]
|
||||
private float reflectionGainDB = 0.0f;
|
||||
|
||||
private float reflectionGainAmplitude = 1.0f;
|
||||
|
||||
[SerializeField] [Range(0.0f, 100000.0f)]
|
||||
private float sourceSize = 0.0f;
|
||||
|
||||
[Tooltip(
|
||||
"Whether Pico Doppler Simulation is enabled for this sound source, which affects both direct and reflection path of it.\n" +
|
||||
" - If you disabled this option before run or build, you cannot turn it on during runtime, since doppler effect unit is not initialized to save memory.")]
|
||||
[SerializeField]
|
||||
private bool enableDoppler = true;
|
||||
|
||||
[Tooltip(
|
||||
"Mode of distance attenuation of this sound source.\n" +
|
||||
" - None && Fixed: Source volume doesn't decrease when source-listener distance increases.\n" +
|
||||
" - Inversed Squared: Source volume decrease when source-listener distance increases, just like the real world.\n" +
|
||||
" - Customized: Don't use it!!!!")]
|
||||
[SerializeField]
|
||||
public SourceAttenuationMode sourceAttenuationMode = SourceAttenuationMode.InverseSquare;
|
||||
|
||||
[Tooltip(
|
||||
"Source volume will not further increase when source-listener distance is less than this.\n" +
|
||||
" - Only effective when source attenuation mode == Inversed Squared")]
|
||||
[SerializeField]
|
||||
public float minAttenuationDistance = 1.0f;
|
||||
|
||||
[Tooltip(
|
||||
"Source volume will not further decrease when source-listener distance is more than this.\n" +
|
||||
" - Only effective when source attenuation mode == Inversed Squared")]
|
||||
[SerializeField]
|
||||
public float maxAttenuationDistance = 100.0f;
|
||||
|
||||
[Tooltip("Determine shape of the radiation polar pattern of this sound source.\n" +
|
||||
" - Alpha = 0 gives you omnidirectional polar pattern\n" +
|
||||
" - Alpha = 0.5 gives you cardioid polar pattern\n" +
|
||||
" - Alpha = 1 gives you figure-8 polar pattern")]
|
||||
[SerializeField] [Range(0.0f, 1.0f)] private float directivityAlpha = 0.0f;
|
||||
|
||||
[Tooltip("Determine width of the radiation polar pattern of this sound source.\n" +
|
||||
" - Larger order gives you narrower radiation pattern.")]
|
||||
[SerializeField] [Range(0.0f, 1000.0f)]
|
||||
private float directivityOrder = 1.0f;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private Mesh directivityDisplayMesh;
|
||||
#endif
|
||||
|
||||
private SourceConfig sourceConfig;
|
||||
private uint sourcePropertyMask = 0;
|
||||
|
||||
private bool isActive;
|
||||
private bool isAudioDSPInProgress = false;
|
||||
|
||||
public bool IsAudioDSPInProgress
|
||||
{
|
||||
get { return isAudioDSPInProgress; }
|
||||
}
|
||||
|
||||
private PXR_Audio_Spatializer_Context context;
|
||||
|
||||
private PXR_Audio_Spatializer_Context Context
|
||||
{
|
||||
get
|
||||
{
|
||||
if (context == null)
|
||||
context = PXR_Audio_Spatializer_Context.Instance;
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
private AudioSource nativeSource;
|
||||
|
||||
private int sourceId = -1;
|
||||
|
||||
private int currentContextUuid = -2;
|
||||
|
||||
private float[] positionArray = new float[3] { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
private float playheadPosition = 0.0f;
|
||||
private bool wasPlaying = false;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (Context != null && Context.Initialized)
|
||||
{
|
||||
if (Context.UUID == currentContextUuid)
|
||||
isActive = true;
|
||||
else
|
||||
RegisterInternal();
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceId = -1;
|
||||
currentContextUuid = -2;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register this audio source in spatializer
|
||||
/// </summary>
|
||||
internal void RegisterInternal()
|
||||
{
|
||||
nativeSource = GetComponent<AudioSource>();
|
||||
|
||||
positionArray[0] = transform.position.x;
|
||||
positionArray[1] = transform.position.y;
|
||||
positionArray[2] = -transform.position.z;
|
||||
|
||||
sourceConfig = new SourceConfig(PXR_Audio.Spatializer.SourceMode.Spatialize);
|
||||
sourcePropertyMask = 0;
|
||||
|
||||
sourceConfig.position.x = positionArray[0];
|
||||
sourceConfig.position.y = positionArray[1];
|
||||
sourceConfig.position.z = positionArray[2];
|
||||
sourceConfig.front.x = transform.forward.x;
|
||||
sourceConfig.front.y = transform.forward.y;
|
||||
sourceConfig.front.z = -transform.forward.z;
|
||||
sourceConfig.up.x = transform.up.x;
|
||||
sourceConfig.up.y = transform.up.y;
|
||||
sourceConfig.up.z = -transform.up.z;
|
||||
sourceConfig.enableDoppler = enableDoppler;
|
||||
sourceGainAmplitude = DB2Mag(sourceGainDB);
|
||||
sourceConfig.sourceGain = sourceGainAmplitude;
|
||||
reflectionGainAmplitude = DB2Mag(reflectionGainDB);
|
||||
sourceConfig.reflectionGain = reflectionGainAmplitude;
|
||||
sourceConfig.radius = sourceSize;
|
||||
sourceConfig.attenuationMode = sourceAttenuationMode;
|
||||
sourceConfig.minAttenuationDistance = minAttenuationDistance;
|
||||
sourceConfig.maxAttenuationDistance = maxAttenuationDistance;
|
||||
sourceConfig.directivityAlpha = directivityAlpha;
|
||||
sourceConfig.directivityOrder = directivityOrder;
|
||||
|
||||
PXR_Audio.Spatializer.Result ret = Context.AddSourceWithConfig(
|
||||
ref sourceConfig,
|
||||
ref sourceId,
|
||||
false);
|
||||
if (ret != PXR_Audio.Spatializer.Result.Success)
|
||||
{
|
||||
Debug.LogError("Failed to add source.");
|
||||
return;
|
||||
}
|
||||
|
||||
isActive = true;
|
||||
currentContextUuid = Context.UUID;
|
||||
|
||||
Debug.Log("Source #" + sourceId + " is added.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resume playing status of this source
|
||||
/// </summary>
|
||||
public void Resume()
|
||||
{
|
||||
nativeSource.time = playheadPosition;
|
||||
if (wasPlaying)
|
||||
{
|
||||
nativeSource.Play();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup source gain in dB
|
||||
/// </summary>
|
||||
/// <param name="gainDB">Gain in dB</param>
|
||||
public void SetGainDB(float gainDB)
|
||||
{
|
||||
// if (Mathf.Abs(gainDB - sourceGainDB) < 1e-7) return;
|
||||
sourceGainDB = gainDB;
|
||||
sourceConfig.sourceGain = sourceGainAmplitude = DB2Mag(gainDB);
|
||||
sourcePropertyMask |= (uint)SourceProperty.SourceGain;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get source gain in dB
|
||||
/// </summary>
|
||||
public float GetGainDB()
|
||||
{
|
||||
return sourceGainDB;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup source gain in Amplitude
|
||||
/// </summary>
|
||||
/// <param name="gainAmplitude">Gain in Amplitude</param>
|
||||
public void SetGainAmplitude(float gainAmplitude)
|
||||
{
|
||||
sourceConfig.sourceGain = sourceGainAmplitude = gainAmplitude;
|
||||
sourceGainDB = Mag2DB(gainAmplitude);
|
||||
sourcePropertyMask |= (uint)SourceProperty.SourceGain;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup source reflection gain in dB
|
||||
/// </summary>
|
||||
/// <param name="gainDB">Gain in dB</param>
|
||||
public void SetReflectionGainDB(float gainDB)
|
||||
{
|
||||
reflectionGainDB = gainDB;
|
||||
sourceConfig.reflectionGain = reflectionGainAmplitude = DB2Mag(gainDB);
|
||||
sourcePropertyMask |= (uint)SourceProperty.ReflectionGain;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get source reflection gain in dB
|
||||
/// </summary>
|
||||
public float GetReflectionGainDB()
|
||||
{
|
||||
return reflectionGainDB;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup source radius in meters
|
||||
/// </summary>
|
||||
/// <param name="radius">source radius in meter</param>
|
||||
public void SetSize(float radius)
|
||||
{
|
||||
sourceConfig.radius = sourceSize = radius;
|
||||
sourcePropertyMask |= (uint)SourceProperty.VolumetricRadius;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get source radius in meters
|
||||
/// </summary>
|
||||
public float GetSize()
|
||||
{
|
||||
return sourceSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turn on/off in-engine doppler effect
|
||||
/// </summary>
|
||||
/// <param name="on">Turn doppler effect on/off </param>
|
||||
public void SetDopplerStatus(bool on)
|
||||
{
|
||||
sourceConfig.enableDoppler = enableDoppler = on;
|
||||
sourcePropertyMask |= (uint)SourceProperty.DopplerOnOff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get in-engine doppler effect status
|
||||
/// </summary>
|
||||
public bool GetDopplerStatus()
|
||||
{
|
||||
return sourceConfig.enableDoppler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get source attenuation mode
|
||||
/// </summary>
|
||||
public SourceAttenuationMode GetAttenuationMode()
|
||||
{
|
||||
return sourceConfig.attenuationMode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup min attenuation range
|
||||
/// </summary>
|
||||
/// <param name="min"> Minimum attenuation range. Source loudness would stop increasing when source-listener
|
||||
/// distance is shorter than this </param>
|
||||
public void SetMinAttenuationRange(float min)
|
||||
{
|
||||
sourceConfig.minAttenuationDistance = minAttenuationDistance = min;
|
||||
sourcePropertyMask |= (uint)SourceProperty.RangeMin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get min attenuation range
|
||||
/// </summary>
|
||||
public float GetMinAttenuationRange()
|
||||
{
|
||||
return sourceConfig.minAttenuationDistance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup max attenuation range
|
||||
/// </summary>
|
||||
/// <param name="max"> Maximum attenuation range. Source loudness would stop decreasing when source-listener
|
||||
/// distance is further than this </param>
|
||||
public void SetMaxAttenuationRange(float max)
|
||||
{
|
||||
sourceConfig.maxAttenuationDistance = maxAttenuationDistance = max;
|
||||
sourcePropertyMask |= (uint)SourceProperty.RangeMax;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get max attenuation range
|
||||
/// </summary>
|
||||
public float GetMaxAttenuationRange()
|
||||
{
|
||||
return sourceConfig.maxAttenuationDistance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup the radiation polar pattern of source, which describes the gain of initial sound wave radiated towards
|
||||
/// different directions. The relation between sound emission direction, alpha, and order can be described as
|
||||
/// follows: Let theta equals the angle between radiation direction and source front direction, the directivity
|
||||
/// gain g is:
|
||||
/// g = (|1 - alpha| + alpha * cos(theta)) ^ order;
|
||||
/// </summary>
|
||||
/// <param name="alpha"> Define the shape of the directivity pattern.
|
||||
/// <param name="order"> Indicates how sharp the source polar pattern is.
|
||||
public void SetDirectivity(float alpha, float order)
|
||||
{
|
||||
sourceConfig.directivityAlpha = directivityAlpha = alpha;
|
||||
sourceConfig.directivityOrder = directivityOrder = order;
|
||||
sourcePropertyMask |= (uint)SourceProperty.Directivity;
|
||||
}
|
||||
|
||||
public float GetDirectivityAlpha()
|
||||
{
|
||||
return sourceConfig.directivityAlpha;
|
||||
}
|
||||
|
||||
public float GetDirectivityOrder()
|
||||
{
|
||||
return sourceConfig.directivityOrder;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (isActive && sourceId >= 0 && context != null && context.Initialized)
|
||||
{
|
||||
if (transform.hasChanged)
|
||||
{
|
||||
sourceConfig.position.x = transform.position.x;
|
||||
sourceConfig.position.y = transform.position.y;
|
||||
sourceConfig.position.z = -transform.position.z;
|
||||
sourceConfig.front.x = transform.forward.x;
|
||||
sourceConfig.front.y = transform.forward.y;
|
||||
sourceConfig.front.z = -transform.forward.z;
|
||||
sourceConfig.up.x = transform.up.x;
|
||||
sourceConfig.up.y = transform.up.y;
|
||||
sourceConfig.up.z = -transform.up.z;
|
||||
|
||||
sourcePropertyMask |= (uint)SourceProperty.Position | (uint)SourceProperty.Orientation;
|
||||
transform.hasChanged = false;
|
||||
}
|
||||
|
||||
if (sourcePropertyMask != 0)
|
||||
{
|
||||
var ret = Context.SetSourceConfig(sourceId, ref sourceConfig, sourcePropertyMask);
|
||||
if (ret == Result.Success)
|
||||
sourcePropertyMask = 0;
|
||||
}
|
||||
|
||||
if (nativeSource.isPlaying)
|
||||
playheadPosition = nativeSource.time;
|
||||
wasPlaying = nativeSource.isPlaying;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
isActive = false;
|
||||
isAudioDSPInProgress = false;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
DestroyInternal();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnValidate()
|
||||
{
|
||||
if (EditorApplication.isPlaying)
|
||||
{
|
||||
SetGainDB(sourceGainDB);
|
||||
SetReflectionGainDB(reflectionGainDB);
|
||||
SetSize(sourceSize);
|
||||
SetDopplerStatus(enableDoppler);
|
||||
SetDirectivity(directivityAlpha, directivityOrder);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
private void DestroyInternal()
|
||||
{
|
||||
isActive = false;
|
||||
if (context != null && context.Initialized)
|
||||
{
|
||||
var ret = context.RemoveSource(sourceId);
|
||||
if (ret != PXR_Audio.Spatializer.Result.Success)
|
||||
{
|
||||
Debug.LogError("Failed to delete source #" + sourceId + ", error code is: " + ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("Source #" + sourceId + " is deleted.");
|
||||
}
|
||||
}
|
||||
|
||||
isAudioDSPInProgress = false;
|
||||
sourceId = -1;
|
||||
}
|
||||
|
||||
private void OnAudioFilterRead(float[] data, int channels)
|
||||
{
|
||||
if (!isActive || sourceId < 0 || context == null || !context.Initialized)
|
||||
{
|
||||
// Mute Original signal
|
||||
for (int i = 0; i < data.Length; ++i)
|
||||
data[i] = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
isAudioDSPInProgress = true;
|
||||
int numFrames = data.Length / channels;
|
||||
float oneOverChannelsF = 1.0f / ((float)channels);
|
||||
|
||||
// force to mono
|
||||
if (channels > 1)
|
||||
{
|
||||
for (int frame = 0; frame < numFrames; ++frame)
|
||||
{
|
||||
float sample = 0.0f;
|
||||
for (int channel = 0; channel < channels; ++channel)
|
||||
{
|
||||
sample += data[frame * channels + channel];
|
||||
}
|
||||
|
||||
data[frame] = sample * oneOverChannelsF;
|
||||
}
|
||||
}
|
||||
|
||||
Context.SubmitSourceBuffer(sourceId, data, (uint)numFrames);
|
||||
|
||||
// Mute Original signal
|
||||
for (int i = 0; i < data.Length; ++i)
|
||||
data[i] = 0.0f;
|
||||
isAudioDSPInProgress = false;
|
||||
}
|
||||
|
||||
private float DB2Mag(float db)
|
||||
{
|
||||
return Mathf.Pow(10.0f, db / 20.0f);
|
||||
}
|
||||
|
||||
private float Mag2DB(float mag)
|
||||
{
|
||||
return 20 * Mathf.Log10(mag);
|
||||
}
|
||||
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
Color c;
|
||||
const float colorSolidAlpha = 0.1f;
|
||||
|
||||
// VolumetricRadius (purple)
|
||||
c.r = 1.0f;
|
||||
c.g = 0.0f;
|
||||
c.b = 1.0f;
|
||||
c.a = 1.0f;
|
||||
Gizmos.color = c;
|
||||
Gizmos.DrawWireSphere(transform.position, sourceSize);
|
||||
c.a = colorSolidAlpha;
|
||||
Gizmos.color = c;
|
||||
Gizmos.DrawSphere(transform.position, sourceSize);
|
||||
|
||||
// Attenuation distance (min && max)
|
||||
if (sourceAttenuationMode == SourceAttenuationMode.InverseSquare)
|
||||
{
|
||||
// min
|
||||
c.r = 1.0f;
|
||||
c.g = 0.35f;
|
||||
c.b = 0.0f;
|
||||
c.a = 1.0f;
|
||||
Gizmos.color = c;
|
||||
Gizmos.DrawWireSphere(transform.position, minAttenuationDistance);
|
||||
c.a = colorSolidAlpha;
|
||||
Gizmos.color = c;
|
||||
Gizmos.DrawSphere(transform.position, minAttenuationDistance);
|
||||
|
||||
// max
|
||||
c.r = 0.0f;
|
||||
c.g = 1.0f;
|
||||
c.b = 1.0f;
|
||||
c.a = 1.0f;
|
||||
Gizmos.color = c;
|
||||
Gizmos.DrawWireSphere(transform.position, maxAttenuationDistance);
|
||||
c.a = colorSolidAlpha;
|
||||
Gizmos.color = c;
|
||||
Gizmos.DrawSphere(transform.position, maxAttenuationDistance);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
// Draw directivity mesh
|
||||
GeneratePolarPatternMesh(directivityDisplayMesh, directivityAlpha, directivityOrder);
|
||||
}
|
||||
|
||||
private void GeneratePolarPatternMesh(Mesh mesh, float alpha, float order)
|
||||
{
|
||||
if (mesh == null)
|
||||
mesh = new Mesh();
|
||||
Vector2[] cardioidVertices2D = GeneratePolarPatternVertices2D(alpha, order, 90);
|
||||
int numVertices = cardioidVertices2D.Length * 2;
|
||||
Vector3[] vertices = new Vector3[numVertices];
|
||||
for (int i = 0; i < cardioidVertices2D.Length; ++i)
|
||||
{
|
||||
var vertex2D = cardioidVertices2D[i];
|
||||
vertices[i] = new Vector3(vertex2D.x, 0.0f, vertex2D.y);
|
||||
vertices[cardioidVertices2D.Length + i] = Quaternion.AngleAxis(45, Vector3.forward) *
|
||||
new Vector3(vertex2D.x, 0.0f, vertex2D.y);
|
||||
}
|
||||
|
||||
int[] indices = new int[cardioidVertices2D.Length * 2 * 3];
|
||||
int idx = 0;
|
||||
for (idx = 0; idx < cardioidVertices2D.Length - 1; ++idx)
|
||||
{
|
||||
indices[idx * 6 + 0] = idx;
|
||||
indices[idx * 6 + 1] = idx + 1;
|
||||
indices[idx * 6 + 2] = idx + cardioidVertices2D.Length;
|
||||
indices[idx * 6 + 3] = idx + 1;
|
||||
indices[idx * 6 + 4] = idx + cardioidVertices2D.Length + 1;
|
||||
indices[idx * 6 + 5] = idx + cardioidVertices2D.Length;
|
||||
}
|
||||
|
||||
// Construct a new mesh for the gizmo.
|
||||
mesh.vertices = vertices;
|
||||
mesh.triangles = indices;
|
||||
mesh.RecalculateNormals();
|
||||
// Draw the mesh.
|
||||
Vector3 scale = 2.0f * Mathf.Max(transform.lossyScale.x, transform.lossyScale.z) * Vector3.one;
|
||||
Color c;
|
||||
c.r = 0.2f;
|
||||
c.g = 0.5f;
|
||||
c.b = 0.7f;
|
||||
c.a = 0.5f;
|
||||
Gizmos.color = c;
|
||||
Gizmos.DrawMesh(mesh, transform.position, transform.rotation, scale);
|
||||
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(45, Vector3.forward),
|
||||
scale);
|
||||
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(90, Vector3.forward),
|
||||
scale);
|
||||
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(135, Vector3.forward),
|
||||
scale);
|
||||
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(180, Vector3.forward),
|
||||
scale);
|
||||
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(225, Vector3.forward),
|
||||
scale);
|
||||
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(270, Vector3.forward),
|
||||
scale);
|
||||
Gizmos.DrawMesh(mesh, transform.position, transform.rotation * Quaternion.AngleAxis(315, Vector3.forward),
|
||||
scale);
|
||||
}
|
||||
|
||||
private Vector2[] GeneratePolarPatternVertices2D(float alpha, float order, int numVertices)
|
||||
{
|
||||
Vector2[] points = new Vector2[numVertices];
|
||||
float interval = Mathf.PI / (numVertices - 1);
|
||||
for (int i = 0; i < numVertices; ++i)
|
||||
{
|
||||
float theta = 0.0f;
|
||||
if (i != numVertices - 1)
|
||||
theta = i * interval;
|
||||
else
|
||||
theta = Mathf.PI;
|
||||
// Magnitude |r| for |theta| in radians.
|
||||
float r = Mathf.Pow(Mathf.Abs((1 - alpha) + alpha * Mathf.Cos(theta)), order);
|
||||
points[i] = new Vector2(r * Mathf.Sin(theta), r * Mathf.Cos(theta));
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30f78ed5e5eb44c188a740990ce49414
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,654 @@
|
||||
// Copyright © 2015-2022 Pico Technology Co., Ltd. All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using PXR_Audio.Spatializer;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
public partial class PXR_Audio_Spatializer_Context : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Audio backend you prefer to use")] [SerializeField]
|
||||
public SpatializerApiImpl spatializerApiImpl = SpatializerApiImpl.unity;
|
||||
|
||||
private static PXR_Audio.Spatializer.Api _api = null;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private static SpatializerApiImpl _lastSpatializerApiImpl;
|
||||
#endif
|
||||
public PXR_Audio.Spatializer.Api PXR_Audio_Spatializer_Api
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (_api == null ||
|
||||
(_lastSpatializerApiImpl != spatializerApiImpl && !EditorApplication.isPlaying))
|
||||
#else
|
||||
if (_api == null)
|
||||
#endif
|
||||
{
|
||||
if (spatializerApiImpl == SpatializerApiImpl.unity)
|
||||
_api = new ApiUnityImpl();
|
||||
else if (spatializerApiImpl == SpatializerApiImpl.wwise)
|
||||
_api = new ApiWwiseImpl();
|
||||
#if UNITY_EDITOR
|
||||
_lastSpatializerApiImpl = spatializerApiImpl;
|
||||
#endif
|
||||
}
|
||||
|
||||
return _api;
|
||||
}
|
||||
}
|
||||
|
||||
private static PXR_Audio_Spatializer_Context _instance;
|
||||
|
||||
public static PXR_Audio_Spatializer_Context Instance => _instance;
|
||||
|
||||
private IntPtr context = IntPtr.Zero;
|
||||
|
||||
private bool initialized = false;
|
||||
|
||||
private bool isSceneDirty = false;
|
||||
|
||||
public bool Initialized
|
||||
{
|
||||
get => initialized;
|
||||
}
|
||||
|
||||
[Tooltip(
|
||||
"Rendering quality for Pico Spatial Audio system. Higher quality gives you better accuracy to real world, while lower quality saves you more computation.\n" +
|
||||
" - You need to re-enable this component after your changed quality during runtime.")]
|
||||
[SerializeField]
|
||||
private PXR_Audio.Spatializer.RenderingMode renderingQuality = PXR_Audio.Spatializer.RenderingMode.MediumQuality;
|
||||
|
||||
#region EDITOR-ONLY SerializedFields
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[SerializeField, HideInInspector] private LayerMask meshBakingLayerMask = ~0;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
public PXR_Audio.Spatializer.RenderingMode RenderingQuality => renderingQuality;
|
||||
|
||||
[Tooltip("Customizable event executed right before Pico Spatial Audio system is initialized for this game.")]
|
||||
[SerializeField]
|
||||
private UnityEvent preInitEvent;
|
||||
|
||||
[Tooltip("Customizable event executed right after Pico Spatial Audio system is initialized for this game.")]
|
||||
[SerializeField]
|
||||
private UnityEvent lateInitEvent;
|
||||
|
||||
private AudioConfiguration audioConfig;
|
||||
|
||||
public AudioConfiguration AudioConfig => audioConfig;
|
||||
|
||||
private bool bypass = true;
|
||||
|
||||
private bool Bypass => bypass;
|
||||
|
||||
static int uuidCounter = 0;
|
||||
|
||||
private static int GetUuid()
|
||||
{
|
||||
var temp = uuidCounter;
|
||||
uuidCounter = (uuidCounter == Int32.MaxValue) ? 0 : (uuidCounter + 1);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private int uuid = -1;
|
||||
public int UUID => uuid;
|
||||
|
||||
public PXR_Audio.Spatializer.Result SubmitMesh(
|
||||
float[] vertices,
|
||||
int verticesCount,
|
||||
int[] indices,
|
||||
int indicesCount,
|
||||
PXR_Audio.Spatializer.AcousticsMaterial material,
|
||||
ref int geometryId)
|
||||
{
|
||||
isSceneDirty = true;
|
||||
return PXR_Audio_Spatializer_Api.SubmitMesh(
|
||||
context,
|
||||
vertices,
|
||||
verticesCount,
|
||||
indices,
|
||||
indicesCount,
|
||||
material,
|
||||
ref geometryId);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SubmitMeshAndMaterialFactor(
|
||||
float[] vertices,
|
||||
int verticesCount,
|
||||
int[] indices,
|
||||
int indicesCount,
|
||||
float[] absorptionFactor,
|
||||
float scatteringFactor,
|
||||
float transmissionFactor,
|
||||
ref int geometryId)
|
||||
{
|
||||
isSceneDirty = true;
|
||||
return PXR_Audio_Spatializer_Api.SubmitMeshAndMaterialFactor(
|
||||
context,
|
||||
vertices,
|
||||
verticesCount,
|
||||
indices,
|
||||
indicesCount,
|
||||
absorptionFactor,
|
||||
scatteringFactor,
|
||||
transmissionFactor,
|
||||
ref geometryId);
|
||||
}
|
||||
|
||||
public Result SubmitMeshWithConfig(float[] vertices, int verticesCount, int[] indices, int indicesCount,
|
||||
ref MeshConfig config, ref int geometryId)
|
||||
{
|
||||
isSceneDirty = true;
|
||||
return PXR_Audio_Spatializer_Api.SubmitMeshWithConfig(context, vertices, verticesCount, indices, indicesCount,
|
||||
ref config, ref geometryId);
|
||||
}
|
||||
|
||||
public Result UpdateMesh(int geometryId, float[] newVertices, int newVerticesCount, int[] newIndices,
|
||||
int newIndicesCount, ref MeshConfig config, ref int newGeometryId)
|
||||
{
|
||||
isSceneDirty = true;
|
||||
return PXR_Audio_Spatializer_Api.UpdateMesh(context, geometryId, newVertices, newVerticesCount, newIndices,
|
||||
newIndicesCount, ref config, ref newGeometryId);
|
||||
}
|
||||
|
||||
public Result RemoveMesh(int geometryId)
|
||||
{
|
||||
isSceneDirty = true;
|
||||
return PXR_Audio_Spatializer_Api.RemoveMesh(context, geometryId);
|
||||
}
|
||||
|
||||
public int GetNumOfGeometries()
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.GetNumOfGeometries(context);
|
||||
}
|
||||
|
||||
public Result SetMeshConfig(int geometryId, ref MeshConfig config, uint propertyMask)
|
||||
{
|
||||
isSceneDirty = true;
|
||||
return PXR_Audio_Spatializer_Api.SetMeshConfig(context, geometryId, ref config, propertyMask);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result AddSource(
|
||||
PXR_Audio.Spatializer.SourceMode sourceMode,
|
||||
float[] position,
|
||||
ref int sourceId,
|
||||
bool isAsync = false)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.AddSource(
|
||||
context,
|
||||
sourceMode,
|
||||
position,
|
||||
ref sourceId,
|
||||
isAsync);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result AddSourceWithOrientation(
|
||||
PXR_Audio.Spatializer.SourceMode mode,
|
||||
float[] position,
|
||||
float[] front,
|
||||
float[] up,
|
||||
float radius,
|
||||
ref int sourceId,
|
||||
bool isAsync)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.AddSourceWithOrientation(
|
||||
context,
|
||||
mode,
|
||||
position,
|
||||
front,
|
||||
up,
|
||||
radius,
|
||||
ref sourceId,
|
||||
isAsync);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result AddSourceWithConfig(
|
||||
ref PXR_Audio.Spatializer.SourceConfig sourceConfig,
|
||||
ref int sourceId,
|
||||
bool isAsync)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.AddSourceWithConfig(context, ref sourceConfig, ref sourceId, isAsync);
|
||||
}
|
||||
|
||||
public Result SetSourceConfig(int sourceId, ref SourceConfig sourceConfig, uint propertyMask)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SetSourceConfig(context, sourceId, ref sourceConfig, propertyMask);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SetSourceAttenuationMode(int sourceId,
|
||||
PXR_Audio.Spatializer.SourceAttenuationMode mode,
|
||||
PXR_Audio.Spatializer.DistanceAttenuationCallback directDistanceAttenuationCallback = null,
|
||||
PXR_Audio.Spatializer.DistanceAttenuationCallback indirectDistanceAttenuationCallback = null)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SetSourceAttenuationMode(context, sourceId, mode,
|
||||
directDistanceAttenuationCallback, indirectDistanceAttenuationCallback);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SetSourceRange(int sourceId, float rangeMin, float rangeMax)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SetSourceRange(context, sourceId, rangeMin, rangeMax);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result RemoveSource(int sourceId)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.RemoveSource(context, sourceId);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SubmitSourceBuffer(
|
||||
int sourceId,
|
||||
float[] inputBufferPtr,
|
||||
uint numFrames)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SubmitSourceBuffer(
|
||||
context,
|
||||
sourceId,
|
||||
inputBufferPtr,
|
||||
numFrames);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SubmitAmbisonicChannelBuffer(
|
||||
float[] ambisonicChannelBuffer,
|
||||
int order,
|
||||
int degree,
|
||||
PXR_Audio.Spatializer.AmbisonicNormalizationType normType,
|
||||
float gain)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SubmitAmbisonicChannelBuffer(
|
||||
context,
|
||||
ambisonicChannelBuffer,
|
||||
order,
|
||||
degree,
|
||||
normType,
|
||||
gain);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SubmitInterleavedAmbisonicBuffer(
|
||||
float[] ambisonicBuffer,
|
||||
int ambisonicOrder,
|
||||
PXR_Audio.Spatializer.AmbisonicNormalizationType normType,
|
||||
float gain)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SubmitInterleavedAmbisonicBuffer(
|
||||
context,
|
||||
ambisonicBuffer,
|
||||
ambisonicOrder,
|
||||
normType,
|
||||
gain);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SubmitMatrixInputBuffer(
|
||||
float[] inputBuffer,
|
||||
int inputChannelIndex)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SubmitMatrixInputBuffer(
|
||||
context,
|
||||
inputBuffer,
|
||||
inputChannelIndex);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result GetInterleavedBinauralBuffer(
|
||||
float[] outputBufferPtr,
|
||||
uint numFrames,
|
||||
bool isAccumulative)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.GetInterleavedBinauralBuffer(
|
||||
context,
|
||||
outputBufferPtr,
|
||||
numFrames,
|
||||
isAccumulative);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result GetPlanarBinauralBuffer(
|
||||
float[][] outputBufferPtr,
|
||||
uint numFrames,
|
||||
bool isAccumulative)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.GetPlanarBinauralBuffer(
|
||||
context,
|
||||
outputBufferPtr,
|
||||
numFrames,
|
||||
isAccumulative);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result GetInterleavedLoudspeakersBuffer(
|
||||
float[] outputBufferPtr,
|
||||
uint numFrames)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.GetInterleavedLoudspeakersBuffer(
|
||||
context,
|
||||
outputBufferPtr,
|
||||
numFrames);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result GetPlanarLoudspeakersBuffer(
|
||||
float[][] outputBufferPtr,
|
||||
uint numFrames)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.GetPlanarLoudspeakersBuffer(
|
||||
context,
|
||||
outputBufferPtr,
|
||||
numFrames);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SetPlaybackMode(
|
||||
PXR_Audio.Spatializer.PlaybackMode playbackMode)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SetPlaybackMode(
|
||||
context,
|
||||
playbackMode);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SetLoudspeakerArray(
|
||||
float[] positions,
|
||||
int numLoudspeakers)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SetLoudspeakerArray(
|
||||
context,
|
||||
positions,
|
||||
numLoudspeakers);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SetMappingMatrix(
|
||||
float[] matrix,
|
||||
int numInputChannels,
|
||||
int numOutputChannels)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SetMappingMatrix(
|
||||
context,
|
||||
matrix,
|
||||
numInputChannels,
|
||||
numOutputChannels);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SetListenerPosition(
|
||||
float[] position)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SetListenerPosition(
|
||||
context,
|
||||
position);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SetListenerOrientation(
|
||||
float[] front,
|
||||
float[] up)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SetListenerOrientation(
|
||||
context,
|
||||
front,
|
||||
up);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SetListenerPose(
|
||||
float[] position,
|
||||
float[] front,
|
||||
float[] up)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SetListenerPose(
|
||||
context,
|
||||
position,
|
||||
front,
|
||||
up);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SetSourcePosition(
|
||||
int sourceId,
|
||||
float[] position)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SetSourcePosition(
|
||||
context,
|
||||
sourceId,
|
||||
position);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SetSourceGain(
|
||||
int sourceId,
|
||||
float gain)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SetSourceGain(
|
||||
context,
|
||||
sourceId,
|
||||
gain);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SetSourceSize(
|
||||
int sourceId,
|
||||
float volumetricSize)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SetSourceSize(
|
||||
context,
|
||||
sourceId,
|
||||
volumetricSize);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result UpdateSourceMode(
|
||||
int sourceId,
|
||||
PXR_Audio.Spatializer.SourceMode mode)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.UpdateSourceMode(
|
||||
context,
|
||||
sourceId,
|
||||
mode);
|
||||
}
|
||||
|
||||
public PXR_Audio.Spatializer.Result SetDopplerEffect(int sourceId, bool on)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.SetDopplerEffect(context, sourceId, on);
|
||||
}
|
||||
|
||||
public Result GetAbsorptionFactors(AcousticsMaterial material,
|
||||
float[] absorptionFactor)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.GetAbsorptionFactor(material, absorptionFactor);
|
||||
}
|
||||
|
||||
public Result GetScatteringFactors(AcousticsMaterial material,
|
||||
ref float scatteringFactor)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.GetScatteringFactor(material, ref scatteringFactor);
|
||||
}
|
||||
|
||||
public Result GetTransmissionFactors(AcousticsMaterial material,
|
||||
ref float transmissionFactor)
|
||||
{
|
||||
return PXR_Audio_Spatializer_Api.GetTransmissionFactor(material, ref transmissionFactor);
|
||||
}
|
||||
|
||||
void OnAudioConfigurationChangedEventHandler(bool deviceWasChanged)
|
||||
{
|
||||
audioConfig = AudioSettings.GetConfiguration();
|
||||
ResetContext(renderingQuality);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup Spatializer rendering quality.
|
||||
/// </summary>
|
||||
/// <param name="quality">Rendering quality preset.</param>
|
||||
public void SetRenderingQuality(PXR_Audio.Spatializer.RenderingMode quality)
|
||||
{
|
||||
renderingQuality = quality;
|
||||
AudioSettings.Reset(AudioSettings.GetConfiguration());
|
||||
Debug.Log("Pico Spatializer has set rendering quality to: " + renderingQuality);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = this;
|
||||
|
||||
AudioSettings.OnAudioConfigurationChanged += OnAudioConfigurationChangedEventHandler;
|
||||
|
||||
// Create context
|
||||
StartInternal(renderingQuality);
|
||||
Debug.Log("Pico Spatializer Initialized.");
|
||||
|
||||
DontDestroyOnLoad(this);
|
||||
}
|
||||
else if (_instance != this)
|
||||
{
|
||||
Destroy(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartInternal(PXR_Audio.Spatializer.RenderingMode quality)
|
||||
{
|
||||
preInitEvent.Invoke();
|
||||
uuid = GetUuid();
|
||||
PXR_Audio.Spatializer.Result ret = Result.Success;
|
||||
|
||||
audioConfig = AudioSettings.GetConfiguration();
|
||||
ret = PXR_Audio_Spatializer_Api.CreateContext(
|
||||
ref context,
|
||||
quality,
|
||||
(uint)audioConfig.dspBufferSize,
|
||||
(uint)audioConfig.sampleRate);
|
||||
if (ret != PXR_Audio.Spatializer.Result.Success)
|
||||
{
|
||||
Debug.LogError("Failed to create context, error code: " + ret);
|
||||
}
|
||||
|
||||
ret = PXR_Audio_Spatializer_Api.InitializeContext(context);
|
||||
if (ret != PXR_Audio.Spatializer.Result.Success)
|
||||
{
|
||||
Debug.LogError("Failed to initialize context, error code: " + ret);
|
||||
}
|
||||
|
||||
// Add all the geometries back
|
||||
PXR_Audio_Spatializer_SceneGeometry[] geometries = FindObjectsOfType<PXR_Audio_Spatializer_SceneGeometry>();
|
||||
for (int geoId = 0; geoId < geometries.Length; ++geoId)
|
||||
{
|
||||
// For all found geometry and material pair, submit them into Pico spatializer
|
||||
geometries[geoId].SubmitMeshToContext();
|
||||
geometries[geoId].SubmitStaticMeshToContext();
|
||||
if (ret != PXR_Audio.Spatializer.Result.Success)
|
||||
{
|
||||
Debug.LogError("Failed to submit geometry #" + geoId + ", error code: " + ret);
|
||||
}
|
||||
}
|
||||
|
||||
ret = PXR_Audio_Spatializer_Api.CommitScene(context);
|
||||
if (ret != PXR_Audio.Spatializer.Result.Success)
|
||||
{
|
||||
Debug.LogError("Failed to commit scene, error code: " + ret);
|
||||
}
|
||||
|
||||
lateInitEvent.Invoke();
|
||||
|
||||
initialized = true;
|
||||
if (spatializerApiImpl != SpatializerApiImpl.wwise)
|
||||
{
|
||||
// Add all the sources back
|
||||
PXR_Audio_Spatializer_AudioSource[] sources = FindObjectsOfType<PXR_Audio_Spatializer_AudioSource>();
|
||||
for (int i = 0; i < sources.Length; ++i)
|
||||
{
|
||||
sources[i].RegisterInternal();
|
||||
}
|
||||
}
|
||||
|
||||
// Add listener back
|
||||
PXR_Audio_Spatializer_AudioListener listener = FindObjectOfType<PXR_Audio_Spatializer_AudioListener>();
|
||||
listener.RegisterInternal();
|
||||
}
|
||||
|
||||
private void DestroyInternal()
|
||||
{
|
||||
initialized = false;
|
||||
uuid = -1;
|
||||
if (spatializerApiImpl == SpatializerApiImpl.wwise)
|
||||
{
|
||||
PXR_Audio_Spatializer_Api.Destroy(context);
|
||||
context = IntPtr.Zero;
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait until all sources and listener's on-going audio DSP process had finished
|
||||
bool canContinue = true;
|
||||
do
|
||||
{
|
||||
canContinue = true;
|
||||
PXR_Audio_Spatializer_AudioListener[] listeners = FindObjectsOfType<PXR_Audio_Spatializer_AudioListener>();
|
||||
foreach (var listener in listeners)
|
||||
{
|
||||
if (listener != null && listener.IsAudioDSPInProgress)
|
||||
{
|
||||
canContinue = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PXR_Audio_Spatializer_AudioSource[] sources = FindObjectsOfType<PXR_Audio_Spatializer_AudioSource>();
|
||||
foreach (var source in sources)
|
||||
{
|
||||
if (source != null && source.IsAudioDSPInProgress)
|
||||
{
|
||||
canContinue = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!canContinue);
|
||||
|
||||
PXR_Audio_Spatializer_Api.Destroy(context);
|
||||
context = IntPtr.Zero;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_instance != null && _instance == this)
|
||||
{
|
||||
_instance = null;
|
||||
|
||||
// Remove context reset handler when destructing context
|
||||
// https://docs.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-add-an-event-handler?view=netdesktop-6.0
|
||||
AudioSettings.OnAudioConfigurationChanged -= OnAudioConfigurationChangedEventHandler;
|
||||
DestroyInternal();
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (isSceneDirty)
|
||||
{
|
||||
PXR_Audio_Spatializer_Api.CommitScene(context);
|
||||
isSceneDirty = false;
|
||||
}
|
||||
|
||||
PXR_Audio_Spatializer_Api.UpdateScene(context);
|
||||
}
|
||||
|
||||
void ResetContext(PXR_Audio.Spatializer.RenderingMode quality)
|
||||
{
|
||||
DestroyInternal();
|
||||
StartInternal(quality);
|
||||
|
||||
if (spatializerApiImpl == SpatializerApiImpl.wwise)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Resume all sources playback
|
||||
var sources = FindObjectsOfType<PXR_Audio_Spatializer_AudioSource>();
|
||||
foreach (var source in sources)
|
||||
{
|
||||
source.Resume();
|
||||
}
|
||||
|
||||
// Resume all ambisonic sources playback
|
||||
var ambisonicSources =
|
||||
FindObjectsOfType<PXR_Audio_Spatializer_AmbisonicSource>();
|
||||
foreach (var source in ambisonicSources)
|
||||
{
|
||||
source.Resume();
|
||||
}
|
||||
|
||||
Debug.Log("Pico Spatializer Context restarted.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3083421a52eca4fb186aecb7e4f143a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using PXR_Audio.Spatializer;
|
||||
using UnityEngine;
|
||||
using Unity.XR.PXR;
|
||||
using UnityEngine.XR;
|
||||
|
||||
public class PXR_Audio_Spatializer_MRSceneGeometryManager : MonoBehaviour
|
||||
{
|
||||
public bool meshUpdate = true;
|
||||
public bool ignoreMeshLabel = false;
|
||||
private static PXR_Audio_Spatializer_MRSceneGeometryManager _instance = null;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
PXR_SpatialMeshManager.MeshAdded += AddAcousticSceneGeometries;
|
||||
if (meshUpdate)
|
||||
PXR_SpatialMeshManager.MeshUpdated += UpdateAcousticSceneGeometries;
|
||||
Debug.Log("PXR_Audio_Spatializer_MRSceneGeometryManager attached");
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
PXR_SpatialMeshManager.MeshAdded -= AddAcousticSceneGeometries;
|
||||
if (meshUpdate)
|
||||
PXR_SpatialMeshManager.MeshUpdated -= UpdateAcousticSceneGeometries;
|
||||
Debug.Log("PXR_Audio_Spatializer_MRSceneGeometryManager detached");
|
||||
}
|
||||
|
||||
private void AddAcousticSceneGeometries(Guid guid, GameObject o)
|
||||
{
|
||||
// Add acoustic mesh
|
||||
var acousticMesh = o.GetComponent<PXR_Audio_Spatializer_SceneGeometry>();
|
||||
var acousticMaterial = o.GetComponent<PXR_Audio_Spatializer_SceneMaterial>();
|
||||
|
||||
if (acousticMesh && acousticMaterial)
|
||||
{
|
||||
UpdateAcousticSceneGeometries(guid, o);
|
||||
}
|
||||
else if (PXR_Plugin.MixedReality.SpatialMeshData.TryGetValue(guid, out var spatialMesh))
|
||||
{
|
||||
var acousticMeshNew = o.AddComponent<PXR_Audio_Spatializer_SceneGeometry>();
|
||||
var acousticMaterialNew = o.GetComponent<PXR_Audio_Spatializer_SceneMaterial>();
|
||||
UpdateMaterialBasedOnLabel(spatialMesh.labels[0], ref acousticMaterialNew);
|
||||
acousticMeshNew.UpdateMaterialType(acousticMaterialNew.materialPreset);
|
||||
acousticMeshNew.UpdateAbsorptionMultiband(acousticMaterialNew.absorption);
|
||||
acousticMeshNew.UpdateScattering(acousticMaterialNew.scattering);
|
||||
acousticMeshNew.UpdateTransmission(acousticMaterialNew.transmission);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAcousticSceneGeometries(Guid guid, GameObject o)
|
||||
{
|
||||
var acousticMesh = o.GetComponent<PXR_Audio_Spatializer_SceneGeometry>();
|
||||
var acousticMaterial = o.GetComponent<PXR_Audio_Spatializer_SceneMaterial>();
|
||||
if (PXR_Plugin.MixedReality.SpatialMeshData.TryGetValue(guid, out var spatialMesh)
|
||||
&& acousticMesh && acousticMaterial)
|
||||
{
|
||||
UpdateMaterialBasedOnLabel(spatialMesh.labels[0], ref acousticMaterial);
|
||||
acousticMesh.UpdateMeshInContext();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMaterialBasedOnLabel(PxrSemanticLabel label, ref PXR_Audio_Spatializer_SceneMaterial material)
|
||||
{
|
||||
if (ignoreMeshLabel)
|
||||
{
|
||||
material.materialPreset = AcousticsMaterial.Custom;
|
||||
material.absorption[0] = 0;
|
||||
material.absorption[1] = 0;
|
||||
material.absorption[2] = 0;
|
||||
material.absorption[3] = 0;
|
||||
material.scattering = 0.2f;
|
||||
material.transmission = 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
AcousticsMaterial acousticsMaterial = AcousticsMaterial.AcousticTile;
|
||||
switch (label)
|
||||
{
|
||||
case PxrSemanticLabel.Floor:
|
||||
acousticsMaterial = AcousticsMaterial.WoodFloor;
|
||||
break;
|
||||
case PxrSemanticLabel.Ceiling:
|
||||
case PxrSemanticLabel.Wall:
|
||||
acousticsMaterial = AcousticsMaterial.PlasterOnConcreteBlock;
|
||||
break;
|
||||
case PxrSemanticLabel.Door:
|
||||
acousticsMaterial = AcousticsMaterial.WoodThin;
|
||||
break;
|
||||
case PxrSemanticLabel.Window:
|
||||
acousticsMaterial = AcousticsMaterial.Glass;
|
||||
break;
|
||||
case PxrSemanticLabel.Opening:
|
||||
material.materialPreset = AcousticsMaterial.Custom;
|
||||
material.absorption[0] = 1;
|
||||
material.absorption[1] = 1;
|
||||
material.absorption[2] = 1;
|
||||
material.absorption[3] = 1;
|
||||
material.scattering = 0;
|
||||
material.transmission = 1;
|
||||
break;
|
||||
case PxrSemanticLabel.Table:
|
||||
acousticsMaterial = AcousticsMaterial.WoodThick;
|
||||
break;
|
||||
case PxrSemanticLabel.Sofa:
|
||||
acousticsMaterial = AcousticsMaterial.AcousticTile;
|
||||
break;
|
||||
case PxrSemanticLabel.Chair:
|
||||
acousticsMaterial = AcousticsMaterial.WoodThin;
|
||||
break;
|
||||
case PxrSemanticLabel.Human:
|
||||
acousticsMaterial = AcousticsMaterial.AcousticTile;
|
||||
break;
|
||||
case PxrSemanticLabel.VirtualWall:
|
||||
acousticsMaterial = AcousticsMaterial.AcousticTile;
|
||||
break;
|
||||
case PxrSemanticLabel.Curtain:
|
||||
acousticsMaterial = AcousticsMaterial.Curtain;
|
||||
break;
|
||||
case PxrSemanticLabel.Cabinet:
|
||||
acousticsMaterial = AcousticsMaterial.WoodThick;
|
||||
break;
|
||||
case PxrSemanticLabel.Bed:
|
||||
acousticsMaterial = AcousticsMaterial.AcousticTile;
|
||||
break;
|
||||
case PxrSemanticLabel.Plant:
|
||||
acousticsMaterial = AcousticsMaterial.Foliage;
|
||||
break;
|
||||
case PxrSemanticLabel.Screen:
|
||||
acousticsMaterial = AcousticsMaterial.Glass;
|
||||
break;
|
||||
case PxrSemanticLabel.Refrigerator:
|
||||
case PxrSemanticLabel.WashingMachine:
|
||||
case PxrSemanticLabel.AirConditioner:
|
||||
acousticsMaterial = AcousticsMaterial.PlasterOnConcreteBlock;
|
||||
break;
|
||||
case PxrSemanticLabel.Lamp:
|
||||
acousticsMaterial = AcousticsMaterial.WoodThin;
|
||||
break;
|
||||
case PxrSemanticLabel.WallArt:
|
||||
acousticsMaterial = AcousticsMaterial.PlasterOnConcreteBlock;
|
||||
break;
|
||||
default:
|
||||
acousticsMaterial = AcousticsMaterial.AcousticTile;
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateMaterialBasedOnAcousticLabel(acousticsMaterial, ref material);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMaterialBasedOnAcousticLabel(AcousticsMaterial acousticLabel,
|
||||
ref PXR_Audio_Spatializer_SceneMaterial material)
|
||||
{
|
||||
if (acousticLabel == AcousticsMaterial.Custom)
|
||||
return;
|
||||
if (PXR_Audio_Spatializer_Context.Instance == null)
|
||||
return;
|
||||
material.materialPreset = AcousticsMaterial.Custom;
|
||||
PXR_Audio_Spatializer_Context.Instance.GetAbsorptionFactors(acousticLabel, material.absorption);
|
||||
PXR_Audio_Spatializer_Context.Instance.GetScatteringFactors(acousticLabel, ref material.scattering);
|
||||
PXR_Audio_Spatializer_Context.Instance.GetTransmissionFactors(acousticLabel, ref material.transmission);
|
||||
material.transmission = Math.Min(material.transmission + 0.5f, 1.0f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8bc043bac160f4079b5ff39f12a5cbcb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,478 @@
|
||||
// Copyright © 2015-2022 Pico Technology Co., Ltd. All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using PXR_Audio.Spatializer;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(PXR_Audio_Spatializer_SceneMaterial))]
|
||||
public class PXR_Audio_Spatializer_SceneGeometry : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Whether include meshes in children game objects as the shape of this acoustic geometry.")]
|
||||
[SerializeField] private bool includeChildren = false;
|
||||
|
||||
[Tooltip("Whether visualize meshes in current scene that are included as the shape of this acoustic geometry.\n" +
|
||||
" - non-static meshes are visualized using white wire frames;\n" +
|
||||
" - static meshes are visualized using green wire frames.")]
|
||||
[SerializeField] private bool visualizeMeshInEditor = false;
|
||||
|
||||
[Tooltip("Baked static mesh used as the shape of this acoustic geometry.")]
|
||||
[SerializeField] private Mesh bakedStaticMesh;
|
||||
|
||||
#region EDITOR-ONLY SerializedFields
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[SerializeField] private LayerMask meshBakingLayerMask = ~0;
|
||||
[SerializeField, HideInInspector] private string currentBakedStaticMeshAssetPath = null;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
public bool isStaticMeshBaked => bakedStaticMesh != null;
|
||||
|
||||
private int geometryId = -1;
|
||||
|
||||
public int GeometryId
|
||||
{
|
||||
get => geometryId;
|
||||
}
|
||||
|
||||
private int staticGeometryID = -1;
|
||||
|
||||
public int StaticGeometryId => staticGeometryID;
|
||||
|
||||
private PXR_Audio_Spatializer_SceneMaterial material;
|
||||
|
||||
public PXR_Audio_Spatializer_SceneMaterial Material
|
||||
{
|
||||
get
|
||||
{
|
||||
if (material == null)
|
||||
{
|
||||
material = GetComponent<PXR_Audio_Spatializer_SceneMaterial>();
|
||||
}
|
||||
|
||||
return material;
|
||||
}
|
||||
}
|
||||
|
||||
private MeshConfig meshConfig;
|
||||
private uint propertyMask = 0;
|
||||
|
||||
private int currentContextUuid = -2;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (PXR_Audio_Spatializer_Context.Instance == null) return;
|
||||
|
||||
// If geometries are added after context is initialized
|
||||
if (PXR_Audio_Spatializer_Context.Instance.UUID != currentContextUuid)
|
||||
{
|
||||
var ret = SubmitMeshToContext();
|
||||
var staticRet = SubmitStaticMeshToContext();
|
||||
}
|
||||
else
|
||||
{
|
||||
meshConfig = new MeshConfig(true, Material, transform.localToWorldMatrix);
|
||||
if (geometryId >= 0)
|
||||
PXR_Audio_Spatializer_Context.Instance.SetMeshConfig(geometryId, ref meshConfig,
|
||||
(uint)MeshProperty.All);
|
||||
if (staticGeometryID >= 0)
|
||||
PXR_Audio_Spatializer_Context.Instance.SetMeshConfig(staticGeometryID, ref meshConfig,
|
||||
(uint)MeshProperty.All);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (PXR_Audio_Spatializer_Context.Instance == null) return;
|
||||
if (PXR_Audio_Spatializer_Context.Instance.UUID != currentContextUuid) return;
|
||||
|
||||
meshConfig.enabled = false;
|
||||
if (geometryId >= 0)
|
||||
PXR_Audio_Spatializer_Context.Instance.SetMeshConfig(geometryId, ref meshConfig,
|
||||
(uint)MeshProperty.Enabled);
|
||||
if (staticGeometryID >= 0)
|
||||
PXR_Audio_Spatializer_Context.Instance.SetMeshConfig(staticGeometryID, ref meshConfig,
|
||||
(uint)MeshProperty.Enabled);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
RemoveMeshFromContext();
|
||||
}
|
||||
|
||||
private void RemoveMeshFromContext()
|
||||
{
|
||||
if (PXR_Audio_Spatializer_Context.Instance == null) return;
|
||||
if (PXR_Audio_Spatializer_Context.Instance.UUID != currentContextUuid) return;
|
||||
if (geometryId >= 0)
|
||||
{
|
||||
PXR_Audio_Spatializer_Context.Instance.RemoveMesh(geometryId);
|
||||
Debug.LogFormat("Removed geometry #{0}, gameObject name is {1}", geometryId.ToString(),
|
||||
name);
|
||||
}
|
||||
|
||||
if (staticGeometryID >= 0)
|
||||
{
|
||||
PXR_Audio_Spatializer_Context.Instance.RemoveMesh(staticGeometryID);
|
||||
Debug.LogFormat("Removed static geometry #{0}, gameObject name is {1}", staticGeometryID.ToString(),
|
||||
name);
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (PXR_Audio_Spatializer_Context.Instance == null) return;
|
||||
|
||||
// // If geometries are added after context is initialized
|
||||
// if (PXR_Audio_Spatializer_Context.Instance.UUID != currentContextUuid)
|
||||
// {
|
||||
// var ret = SubmitMeshToContext();
|
||||
// var staticRet = SubmitStaticMeshToContext();
|
||||
// }
|
||||
|
||||
if (transform.hasChanged)
|
||||
{
|
||||
meshConfig.SetTransformMatrix4x4(transform.localToWorldMatrix);
|
||||
propertyMask |= (uint)MeshProperty.ToWorldTransform;
|
||||
transform.hasChanged = false;
|
||||
}
|
||||
|
||||
if (propertyMask > 0)
|
||||
{
|
||||
if (geometryId >= 0)
|
||||
PXR_Audio_Spatializer_Context.Instance.SetMeshConfig(geometryId, ref meshConfig,
|
||||
propertyMask);
|
||||
if (staticGeometryID >= 0)
|
||||
PXR_Audio_Spatializer_Context.Instance.SetMeshConfig(staticGeometryID, ref meshConfig,
|
||||
propertyMask);
|
||||
|
||||
propertyMask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateAbsorptionMultiband(float[] absorptions)
|
||||
{
|
||||
meshConfig.materialType = AcousticsMaterial.Custom;
|
||||
meshConfig.absorption.v0 = Material.absorption[0] = absorptions[0];
|
||||
meshConfig.absorption.v1 = Material.absorption[1] = absorptions[1];
|
||||
meshConfig.absorption.v2 = Material.absorption[2] = absorptions[2];
|
||||
meshConfig.absorption.v3 = Material.absorption[3] = absorptions[3];
|
||||
propertyMask |= (uint)MeshProperty.Material | (uint)MeshProperty.Absorption;
|
||||
}
|
||||
|
||||
public void UpdateScattering(float scattering)
|
||||
{
|
||||
meshConfig.materialType = AcousticsMaterial.Custom;
|
||||
meshConfig.scattering = Material.scattering = scattering;
|
||||
propertyMask |= (uint)MeshProperty.Material | (uint)MeshProperty.Scattering;
|
||||
}
|
||||
|
||||
public void UpdateTransmission(float transmission)
|
||||
{
|
||||
meshConfig.materialType = AcousticsMaterial.Custom;
|
||||
meshConfig.transmission = Material.transmission = transmission;
|
||||
propertyMask |= (uint)MeshProperty.Material | (uint)MeshProperty.Transmission;
|
||||
}
|
||||
|
||||
public void UpdateMaterialType(PXR_Audio.Spatializer.AcousticsMaterial materialType)
|
||||
{
|
||||
meshConfig.materialType = materialType;
|
||||
propertyMask |= (uint)MeshProperty.Material;
|
||||
}
|
||||
|
||||
private void GetAllMeshFilter(Transform transform, bool includeChildren, List<MeshFilter> meshFilterList,
|
||||
bool isStatic, LayerMask layerMask)
|
||||
{
|
||||
if (includeChildren)
|
||||
{
|
||||
int childCount = transform.childCount;
|
||||
for (int i = 0; i < childCount; i++)
|
||||
{
|
||||
var childTransform = transform.GetChild(i);
|
||||
if (childTransform.GetComponent<PXR_Audio_Spatializer_SceneGeometry>() == null)
|
||||
{
|
||||
GetAllMeshFilter(childTransform.transform, includeChildren, meshFilterList, isStatic, layerMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gather this mesh only when
|
||||
// 1. Its isStatic flag is equal to our requirement
|
||||
// 2. Its layer belongs to layerMask set
|
||||
if (((1 << transform.gameObject.layer) & layerMask) != 0)
|
||||
{
|
||||
var meshFilterArray = transform.GetComponents<MeshFilter>();
|
||||
// cases we don't add to mesh filter list
|
||||
// 1. meshFilter.sharedmesh == null
|
||||
// 2. meshFilter.sharedmesh.isReadable == false
|
||||
if (meshFilterArray != null)
|
||||
{
|
||||
foreach (var meshFilter in meshFilterArray)
|
||||
{
|
||||
if (meshFilter != null && meshFilter.sharedMesh != null &&
|
||||
(
|
||||
(isStatic && (transform.gameObject.isStatic || !meshFilter.sharedMesh.isReadable)) ||
|
||||
(!isStatic && (!transform.gameObject.isStatic && meshFilter.sharedMesh.isReadable))
|
||||
))
|
||||
{
|
||||
meshFilterList.Add(meshFilter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Mesh CombineMeshes(List<MeshFilter> meshFilterList, Transform rootTransform)
|
||||
{
|
||||
if (meshFilterList.Count == 1)
|
||||
return meshFilterList[0].mesh;
|
||||
|
||||
Mesh combinedMesh = new Mesh
|
||||
{
|
||||
name = "combined meshes",
|
||||
indexFormat = UnityEngine.Rendering.IndexFormat.UInt32
|
||||
};
|
||||
|
||||
var combinedVertices = Array.Empty<Vector3>();
|
||||
var combinedIndices = Array.Empty<int>();
|
||||
// Accumulate combined vertices buffer size
|
||||
foreach (var meshFilter in meshFilterList)
|
||||
{
|
||||
int vertexOffset = combinedVertices.Length;
|
||||
combinedVertices = combinedVertices.Concat(meshFilter.sharedMesh.vertices).ToArray();
|
||||
int vertexSegmentEnd = combinedVertices.Length;
|
||||
var toWorld = rootTransform.worldToLocalMatrix *
|
||||
meshFilter.transform.localToWorldMatrix;
|
||||
for (int i = vertexOffset; i < vertexSegmentEnd; ++i)
|
||||
{
|
||||
combinedVertices[i] = toWorld.MultiplyPoint3x4(combinedVertices[i]);
|
||||
}
|
||||
|
||||
var trianglesStartIdx = combinedIndices.Length;
|
||||
combinedIndices = combinedIndices.Concat(meshFilter.sharedMesh.triangles).ToArray();
|
||||
var trianglesEndIdx = combinedIndices.Length;
|
||||
for (var i = trianglesStartIdx; i < trianglesEndIdx; ++i)
|
||||
{
|
||||
combinedIndices[i] += vertexOffset;
|
||||
}
|
||||
}
|
||||
|
||||
combinedMesh.vertices = combinedVertices;
|
||||
combinedMesh.triangles = combinedIndices;
|
||||
combinedMesh.RecalculateNormals();
|
||||
|
||||
return combinedMesh;
|
||||
}
|
||||
|
||||
private static float[] FlattenVerticesBuffer(Vector3[] verticesBuffer)
|
||||
{
|
||||
float[] vertices = new float[verticesBuffer.Length * 3];
|
||||
int index = 0;
|
||||
foreach (Vector3 vertex in verticesBuffer)
|
||||
{
|
||||
vertices[index++] = vertex.x;
|
||||
vertices[index++] = vertex.y;
|
||||
vertices[index++] = vertex.z;
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submit non-static mesh of this geometry and its material into spatializer engine context
|
||||
/// </summary>
|
||||
/// <returns>Result of static mesh submission</returns>
|
||||
public PXR_Audio.Spatializer.Result SubmitMeshToContext(bool showLog = true)
|
||||
{
|
||||
// find all meshes
|
||||
var meshFilterList = new List<MeshFilter>();
|
||||
GetAllMeshFilter(transform, includeChildren, meshFilterList, false, ~0);
|
||||
|
||||
// Combine all meshes
|
||||
Mesh combinedMesh = CombineMeshes(meshFilterList, transform);
|
||||
|
||||
// flatten vertices buffer into a float array
|
||||
float[] vertices = FlattenVerticesBuffer(combinedMesh.vertices);
|
||||
|
||||
meshConfig = new MeshConfig(enabled, Material, transform.localToWorldMatrix);
|
||||
|
||||
// Submit all meshes
|
||||
PXR_Audio.Spatializer.Result result = PXR_Audio_Spatializer_Context.Instance.SubmitMeshWithConfig(
|
||||
vertices, vertices.Length / 3,
|
||||
combinedMesh.triangles, combinedMesh.triangles.Length / 3,
|
||||
ref meshConfig, ref geometryId);
|
||||
|
||||
if (showLog)
|
||||
{
|
||||
if (result != Result.Success)
|
||||
Debug.LogError("Failed to submit audio mesh: " + gameObject.name + ", Error code is: " + result);
|
||||
else
|
||||
Debug.LogFormat("Submitted geometry #{0}, gameObject name is {1}", geometryId.ToString(),
|
||||
name);
|
||||
}
|
||||
|
||||
if (result == Result.Success)
|
||||
currentContextUuid = PXR_Audio_Spatializer_Context.Instance.UUID;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Submit static mesh of this geometry and its material into spatializer engine context
|
||||
/// </summary>
|
||||
/// <returns>Result of static mesh submission</returns>
|
||||
public PXR_Audio.Spatializer.Result SubmitStaticMeshToContext(bool showLog = true)
|
||||
{
|
||||
PXR_Audio.Spatializer.Result result = Result.Success;
|
||||
if (bakedStaticMesh != null)
|
||||
{
|
||||
float[] tempVertices = FlattenVerticesBuffer(bakedStaticMesh.vertices);
|
||||
|
||||
meshConfig = new MeshConfig(enabled, Material, transform.localToWorldMatrix);
|
||||
|
||||
result = PXR_Audio_Spatializer_Context.Instance.SubmitMeshWithConfig(tempVertices,
|
||||
bakedStaticMesh.vertices.Length, bakedStaticMesh.triangles,
|
||||
bakedStaticMesh.triangles.Length / 3, ref meshConfig,
|
||||
ref staticGeometryID);
|
||||
|
||||
if (showLog)
|
||||
{
|
||||
if (result != Result.Success)
|
||||
Debug.LogError("Failed to submit static audio mesh: " + gameObject.name + ", Error code is: " +
|
||||
result);
|
||||
else
|
||||
Debug.LogFormat("Submitted static geometry #{0}, gameObject name is {1}", staticGeometryID.ToString(),
|
||||
name);
|
||||
}
|
||||
}
|
||||
|
||||
if (result == Result.Success)
|
||||
currentContextUuid = PXR_Audio_Spatializer_Context.Instance.UUID;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Result UpdateMeshInContext()
|
||||
{
|
||||
// find all meshes
|
||||
var meshFilterList = new List<MeshFilter>();
|
||||
GetAllMeshFilter(transform, includeChildren, meshFilterList, false, ~0);
|
||||
|
||||
// Combine all meshes
|
||||
Mesh combinedMesh = CombineMeshes(meshFilterList, transform);
|
||||
|
||||
// flatten vertices buffer into a float array
|
||||
float[] vertices = FlattenVerticesBuffer(combinedMesh.vertices);
|
||||
|
||||
meshConfig = new MeshConfig(enabled, Material, transform.localToWorldMatrix);
|
||||
|
||||
// Submit all meshes
|
||||
Result result = PXR_Audio_Spatializer_Context.Instance.UpdateMesh(geometryId,
|
||||
vertices, vertices.Length / 3,
|
||||
combinedMesh.triangles, combinedMesh.triangles.Length / 3,
|
||||
ref meshConfig, ref geometryId);
|
||||
|
||||
if (result == Result.Success)
|
||||
currentContextUuid = PXR_Audio_Spatializer_Context.Instance.UUID;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public int BakeStaticMesh(LayerMask layerMask)
|
||||
{
|
||||
List<MeshFilter> meshList = new List<MeshFilter>();
|
||||
GetAllMeshFilter(transform, includeChildren, meshList, true, meshBakingLayerMask);
|
||||
|
||||
SerializedObject serializedObject = new SerializedObject(this);
|
||||
if (meshList.Count == 0)
|
||||
{
|
||||
bakedStaticMesh = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
bakedStaticMesh = CombineMeshes(meshList, transform);
|
||||
bakedStaticMesh.name = "baked mesh for ygg";
|
||||
}
|
||||
|
||||
serializedObject.FindProperty("bakedStaticMesh").objectReferenceValue = bakedStaticMesh;
|
||||
|
||||
if (bakedStaticMesh != null)
|
||||
{
|
||||
System.IO.Directory.CreateDirectory("Assets/Resources/PxrAudioSpatializerBakedSceneMeshes/");
|
||||
if (!string.IsNullOrEmpty(currentBakedStaticMeshAssetPath))
|
||||
{
|
||||
AssetDatabase.DeleteAsset(currentBakedStaticMeshAssetPath);
|
||||
}
|
||||
|
||||
currentBakedStaticMeshAssetPath = "Assets/Resources/PxrAudioSpatializerBakedSceneMeshes/" + name + "_" +
|
||||
GetInstanceID() + "_" +
|
||||
System.DateTime.UtcNow.ToBinary() + ".yggmesh";
|
||||
serializedObject.FindProperty("currentBakedStaticMeshAssetPath").stringValue =
|
||||
currentBakedStaticMeshAssetPath;
|
||||
AssetDatabase.CreateAsset(bakedStaticMesh, currentBakedStaticMeshAssetPath);
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
return meshList.Count;
|
||||
}
|
||||
|
||||
public void ClearBakeStaticMesh()
|
||||
{
|
||||
SerializedObject serializedObject = new SerializedObject(this);
|
||||
bakedStaticMesh = null;
|
||||
serializedObject.FindProperty("bakedStaticMesh").objectReferenceValue = null;
|
||||
if (!string.IsNullOrEmpty(currentBakedStaticMeshAssetPath))
|
||||
{
|
||||
AssetDatabase.DeleteAsset(currentBakedStaticMeshAssetPath);
|
||||
currentBakedStaticMeshAssetPath = null;
|
||||
serializedObject.FindProperty("currentBakedStaticMeshAssetPath").stringValue =
|
||||
currentBakedStaticMeshAssetPath;
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
#endif
|
||||
|
||||
public void OnDrawGizmos()
|
||||
{
|
||||
if (visualizeMeshInEditor)
|
||||
{
|
||||
// Visualize non-static meshes
|
||||
// find all MeshFilter
|
||||
var meshFilterList = new List<MeshFilter>();
|
||||
GetAllMeshFilter(transform, includeChildren, meshFilterList, false, ~0);
|
||||
|
||||
for (int i = 0; i < meshFilterList.Count; i++)
|
||||
{
|
||||
var mesh = meshFilterList[i].sharedMesh;
|
||||
var transform = meshFilterList[i].transform;
|
||||
Gizmos.DrawWireMesh(mesh,
|
||||
transform.position, transform.rotation, transform.localScale);
|
||||
}
|
||||
|
||||
// Visualize baked static meshes
|
||||
if (isStaticMeshBaked)
|
||||
{
|
||||
Color colorBackUp = Gizmos.color;
|
||||
Color c;
|
||||
c.r = 0.0f;
|
||||
c.g = 0.7f;
|
||||
c.b = 0.0f;
|
||||
c.a = 1.0f;
|
||||
Gizmos.color = c;
|
||||
var gizmosMatrixBackup = Gizmos.matrix;
|
||||
Gizmos.matrix = transform.localToWorldMatrix;
|
||||
Gizmos.DrawWireMesh(bakedStaticMesh);
|
||||
Gizmos.color = colorBackUp;
|
||||
Gizmos.matrix = gizmosMatrixBackup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 555afd84b04c3471788b8eada321c090
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright © 2015-2022 Pico Technology Co., Ltd. All Rights Reserved.
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
public class PXR_Audio_Spatializer_SceneMaterial : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Acoustic material preset corresponding to real-world material types. " +
|
||||
"Absorption, scattering, and transmission will be automatically set based on the physical nature of the selected material.\n" +
|
||||
"Deviating acoustic parameters away from preset values will automatically set this field to 'Custom'")]
|
||||
[SerializeField]
|
||||
public PXR_Audio.Spatializer.AcousticsMaterial
|
||||
materialPreset = PXR_Audio.Spatializer.AcousticsMaterial.AcousticTile;
|
||||
|
||||
private PXR_Audio.Spatializer.AcousticsMaterial lastMaterialPreset =
|
||||
PXR_Audio.Spatializer.AcousticsMaterial.AcousticTile;
|
||||
|
||||
[SerializeField] [Range(0.0f, 1.0f)]
|
||||
public float[] absorption = new float[4];
|
||||
|
||||
[Tooltip("Ratio of sound energy get scattered by each reflection.\n" +
|
||||
" - Low scattering will result in a more echoic sound\n" +
|
||||
" - high scattering will result in a more reverberant sound")]
|
||||
[SerializeField]
|
||||
[Range(0.0f, 1.0f)]
|
||||
public float scattering = 0.0f;
|
||||
|
||||
[Tooltip("Ratio of sound energy get transmitted through this material.")] [SerializeField] [Range(0.0f, 1.0f)]
|
||||
public float transmission = 0.0f;
|
||||
|
||||
private float[] absorptionForValidation = new float[4];
|
||||
private float scatteringForValidation = 0.0f;
|
||||
private float transmissionForValidation = 0.0f;
|
||||
|
||||
private static PXR_Audio_Spatializer_Context spatialAudioContextRef;
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
if (spatialAudioContextRef == null)
|
||||
spatialAudioContextRef = FindObjectOfType<PXR_Audio_Spatializer_Context>();
|
||||
if (lastMaterialPreset != materialPreset) // material_preset is changed
|
||||
{
|
||||
if (materialPreset != PXR_Audio.Spatializer.AcousticsMaterial.Custom)
|
||||
{
|
||||
if (spatialAudioContextRef != null)
|
||||
{
|
||||
spatialAudioContextRef.PXR_Audio_Spatializer_Api.GetAbsorptionFactor(materialPreset,
|
||||
absorption);
|
||||
spatialAudioContextRef.PXR_Audio_Spatializer_Api.GetScatteringFactor(materialPreset,
|
||||
ref scattering);
|
||||
spatialAudioContextRef.PXR_Audio_Spatializer_Api.GetTransmissionFactor(
|
||||
materialPreset, ref transmission);
|
||||
lastMaterialPreset = materialPreset;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lastMaterialPreset = materialPreset;
|
||||
}
|
||||
}
|
||||
else if (materialPreset != PXR_Audio.Spatializer.AcousticsMaterial.Custom &&
|
||||
spatialAudioContextRef !=
|
||||
null) // material_preset is not changed, but acoustic properties are changed manually
|
||||
{
|
||||
// Check if actual material parameters are different from current materialPreset
|
||||
spatialAudioContextRef.PXR_Audio_Spatializer_Api.GetAbsorptionFactor(materialPreset,
|
||||
absorptionForValidation);
|
||||
spatialAudioContextRef.PXR_Audio_Spatializer_Api.GetScatteringFactor(materialPreset,
|
||||
ref scatteringForValidation);
|
||||
spatialAudioContextRef.PXR_Audio_Spatializer_Api.GetTransmissionFactor(materialPreset,
|
||||
ref transmissionForValidation);
|
||||
|
||||
if (Mathf.Abs(absorption[0] - absorptionForValidation[0]) > float.Epsilon ||
|
||||
Mathf.Abs(absorption[1] - absorptionForValidation[1]) > float.Epsilon ||
|
||||
Mathf.Abs(absorption[2] - absorptionForValidation[2]) > float.Epsilon ||
|
||||
Mathf.Abs(absorption[3] - absorptionForValidation[3]) > float.Epsilon ||
|
||||
Mathf.Abs(scattering - scatteringForValidation) > float.Epsilon ||
|
||||
Mathf.Abs(transmission - transmissionForValidation) > float.Epsilon)
|
||||
{
|
||||
materialPreset = PXR_Audio.Spatializer.AcousticsMaterial.Custom;
|
||||
lastMaterialPreset = PXR_Audio.Spatializer.AcousticsMaterial.Custom;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22d37993d4d3e4235b77205c1aa2ff46
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,364 @@
|
||||
// Copyright © 2015-2022 Pico Technology Co., Ltd. All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace PXR_Audio
|
||||
{
|
||||
namespace Spatializer
|
||||
{
|
||||
public enum Result
|
||||
{
|
||||
Error = -1,
|
||||
Success = 0,
|
||||
SourceNotFound = -1001,
|
||||
SourceDataNotFound = -1002,
|
||||
SceneNotFound = -1003,
|
||||
SceneMeshNotFound = -1004,
|
||||
IllegalValue = -1005,
|
||||
ContextNotCreated = -1006,
|
||||
ContextNotReady = -1007,
|
||||
ContextRepeatedInitialization = -1008,
|
||||
EnvironmentalAcousticsDisabled = -1009,
|
||||
ApiDisabled = -1010,
|
||||
|
||||
///< API is disabled in current build
|
||||
SourceInuse = -1011,
|
||||
};
|
||||
|
||||
public enum PlaybackMode
|
||||
{
|
||||
BinauralOut,
|
||||
LoudspeakersOut,
|
||||
};
|
||||
|
||||
public enum LateReverbUpdatingMode
|
||||
{
|
||||
RealtimeLateReverb = 0,
|
||||
BakedLateReverb = 1,
|
||||
SharedSpectralLateReverb = 2,
|
||||
};
|
||||
|
||||
public enum LateReverbRenderingMode
|
||||
{
|
||||
IrLateReverb = 0,
|
||||
SpectralLateReverb = 1,
|
||||
};
|
||||
|
||||
public enum RenderingMode
|
||||
{
|
||||
LowQuality = 0, // 1st order ambisonic
|
||||
MediumQuality = 1, // 3rd order ambisonic
|
||||
HighQuality = 2, // 5th order ambisonic
|
||||
AmbisonicFirstOrder,
|
||||
AmbisonicSecondOrder,
|
||||
AmbisonicThirdOrder,
|
||||
AmbisonicFourthOrder,
|
||||
AmbisonicFifthOrder,
|
||||
AmbisonicSixthOrder,
|
||||
AmbisonicSeventhOrder,
|
||||
};
|
||||
|
||||
public enum SourceMode
|
||||
{
|
||||
Spatialize = 0,
|
||||
Bypass = 1,
|
||||
};
|
||||
|
||||
public enum IRUpdateMethod
|
||||
{
|
||||
PerPartitionSwapping = 0,
|
||||
InterPartitionLinearCrossFade = 1,
|
||||
InterPartitionPowerComplementaryCrossFade = 2
|
||||
};
|
||||
|
||||
public enum AcousticsMaterial
|
||||
{
|
||||
AcousticTile,
|
||||
Brick,
|
||||
BrickPainted,
|
||||
Carpet,
|
||||
CarpetHeavy,
|
||||
CarpetHeavyPadded,
|
||||
CeramicTile,
|
||||
Concrete,
|
||||
ConcreteRough,
|
||||
ConcreteBlock,
|
||||
ConcreteBlockPainted,
|
||||
Curtain,
|
||||
Foliage,
|
||||
Glass,
|
||||
GlassHeavy,
|
||||
Grass,
|
||||
Gravel,
|
||||
GypsumBoard,
|
||||
PlasterOnBrick,
|
||||
PlasterOnConcreteBlock,
|
||||
Soil,
|
||||
SoundProof,
|
||||
Snow,
|
||||
Steel,
|
||||
Water,
|
||||
WoodThin,
|
||||
WoodThick,
|
||||
WoodFloor,
|
||||
WoodOnConcrete,
|
||||
Custom
|
||||
};
|
||||
|
||||
public enum AmbisonicNormalizationType
|
||||
{
|
||||
SN3D,
|
||||
N3D
|
||||
};
|
||||
|
||||
public enum SourceAttenuationMode
|
||||
{
|
||||
None = 0, // 引擎不依据距离计算衰减
|
||||
Fixed = 1, // 与None完全一致
|
||||
InverseSquare = 2, // 引擎 InverseSquare Law 计算距离衰减
|
||||
Customized = 3, // 依据外部传入的 Callback 计算距离衰减
|
||||
};
|
||||
|
||||
public enum SpatializerApiImpl
|
||||
{
|
||||
unity,
|
||||
wwise,
|
||||
}
|
||||
|
||||
public delegate float DistanceAttenuationCallback(float distance, float rangeMin, float rangeMax);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct NativeVector3f
|
||||
{
|
||||
public float x; //float[3]
|
||||
public float y;
|
||||
public float z;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SourceConfig
|
||||
{
|
||||
[MarshalAs(UnmanagedType.U4)] public SourceMode mode;
|
||||
public NativeVector3f position;
|
||||
public NativeVector3f front;
|
||||
public NativeVector3f up;
|
||||
|
||||
public float directivityAlpha; // Weighting balance between figure of eight pattern and circular pattern for
|
||||
|
||||
// source emission in range [0, 1].
|
||||
// A value of 0 results in a circular pattern.
|
||||
// A value of 0.5 results in a cardioid pattern.
|
||||
// A value of 1 results in a figure of eight pattern.
|
||||
public float
|
||||
directivityOrder; // Order applied to computed directivity. Higher values will result in narrower and
|
||||
|
||||
public float radius;
|
||||
|
||||
// sharper directivity patterns. Range [1, inf).
|
||||
[MarshalAs(UnmanagedType.U1)] public bool useDirectPathSpread;
|
||||
|
||||
public float directPathSpread; // Alternatively, we could use spread param directly.
|
||||
|
||||
// This is useful when audio middleware specifies spread value by itself.
|
||||
public float sourceGain; // Master gain of sound source.
|
||||
public float reflectionGain; // Reflection gain relative to default (master gain).
|
||||
|
||||
[MarshalAs(UnmanagedType.U1)] public bool enableDoppler;
|
||||
|
||||
[MarshalAs(UnmanagedType.U4)] public SourceAttenuationMode attenuationMode;
|
||||
|
||||
public IntPtr
|
||||
directDistanceAttenuationCallback; // Native function pointer of direct sound distance attenuation
|
||||
|
||||
public IntPtr indirectDistanceAttenuationCallback;
|
||||
|
||||
// Attenuation range
|
||||
public float minAttenuationDistance; // When distance < minAttenuationDistance, no attenuation.
|
||||
|
||||
public float
|
||||
maxAttenuationDistance; // When distance > maxAttenuationDistance, attenuation = AttenuationFunc(range_max).
|
||||
|
||||
public SourceConfig(SourceMode inMode)
|
||||
{
|
||||
mode = inMode;
|
||||
position.x = 0.0f;
|
||||
position.y = 0.0f;
|
||||
position.z = 0.0f;
|
||||
front.x = 0.0f;
|
||||
front.y = 0.0f;
|
||||
front.z = -1.0f;
|
||||
up.x = 0.0f;
|
||||
up.y = 1.0f;
|
||||
up.z = 0.0f;
|
||||
radius = 0.1f;
|
||||
directivityAlpha = 0.0f;
|
||||
directivityOrder = 1.0f;
|
||||
useDirectPathSpread = false;
|
||||
directPathSpread = 0.0f;
|
||||
sourceGain = 1.0f;
|
||||
reflectionGain = 1.0f;
|
||||
enableDoppler = false;
|
||||
attenuationMode = SourceAttenuationMode.InverseSquare;
|
||||
directDistanceAttenuationCallback = IntPtr.Zero;
|
||||
indirectDistanceAttenuationCallback = IntPtr.Zero;
|
||||
minAttenuationDistance = 0.25f;
|
||||
maxAttenuationDistance = 250f;
|
||||
}
|
||||
}
|
||||
|
||||
public enum SourceProperty : uint
|
||||
{
|
||||
Mode = 1u,
|
||||
Position = (1u << 1),
|
||||
|
||||
///< float[3]
|
||||
Orientation = (1u << 2),
|
||||
|
||||
///< float[6]
|
||||
Directivity = (1u << 3),
|
||||
|
||||
///< float[2], directivity alpha and directivity order
|
||||
VolumetricRadius = (1u << 4),
|
||||
VolumetricSpread = (1u << 5),
|
||||
SourceGain = (1u << 6),
|
||||
ReflectionGain = (1u << 7),
|
||||
DopplerOnOff = (1u << 8),
|
||||
AttenuationMode = (1u << 9),
|
||||
|
||||
///< Only after setting AttenuationMode will AttenuationCallback be applied
|
||||
DirectAttenuationCallback = (1u << 10),
|
||||
IndirectAttenuationCallback = (1u << 11),
|
||||
RangeMin = (1u << 12),
|
||||
RangeMax = (1u << 13),
|
||||
All = ~0u,
|
||||
None = 0u,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct NativeVector4f
|
||||
{
|
||||
public float v0; //float[4]
|
||||
public float v1;
|
||||
public float v2;
|
||||
public float v3;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct NativeMatrix4x4f
|
||||
{
|
||||
public float v0; //float[16]
|
||||
public float v1;
|
||||
public float v2;
|
||||
public float v3;
|
||||
public float v4; //float[16]
|
||||
public float v5;
|
||||
public float v6;
|
||||
public float v7;
|
||||
public float v8; //float[16]
|
||||
public float v9;
|
||||
public float v10;
|
||||
public float v11;
|
||||
public float v12;
|
||||
public float v13;
|
||||
public float v14;
|
||||
public float v15;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct MeshConfig
|
||||
{
|
||||
[MarshalAs(UnmanagedType.U1)] public bool enabled;
|
||||
[MarshalAs(UnmanagedType.U4)] public AcousticsMaterial materialType;
|
||||
|
||||
///< Material preset; If this equal to YGG_MATERIAL_Custom, the absorption,
|
||||
///< scattering, and transmission coefficients below will be used
|
||||
public NativeVector4f absorption;
|
||||
|
||||
///< Absorption of 4 bands
|
||||
public float scattering;
|
||||
|
||||
///< Wide-band scattering
|
||||
public float transmission;
|
||||
|
||||
///< Wide-band transmission
|
||||
public NativeMatrix4x4f toWorldTransform;
|
||||
|
||||
///< Column-major 4x4 to-world transform matrix of this mesh, which
|
||||
///< describes the position, rotation, and scale of it's default to
|
||||
///< identity matrix, which represents a scene mesh positioned at
|
||||
///< world origin, with no rotation and no scaling what's so ever
|
||||
public MeshConfig(bool enabled, PXR_Audio_Spatializer_SceneMaterial material, Matrix4x4 toWorldMatrix4X4)
|
||||
{
|
||||
this.enabled = enabled;
|
||||
materialType = material.materialPreset;
|
||||
absorption.v0 = material.absorption[0];
|
||||
absorption.v1 = material.absorption[1];
|
||||
absorption.v2 = material.absorption[2];
|
||||
absorption.v3 = material.absorption[3];
|
||||
scattering = material.scattering;
|
||||
transmission = material.transmission;
|
||||
toWorldTransform.v0 = toWorldMatrix4X4[0];
|
||||
toWorldTransform.v1 = toWorldMatrix4X4[1];
|
||||
toWorldTransform.v2 = -toWorldMatrix4X4[2];
|
||||
toWorldTransform.v3 = toWorldMatrix4X4[3];
|
||||
toWorldTransform.v4 = toWorldMatrix4X4[4];
|
||||
toWorldTransform.v5 = toWorldMatrix4X4[5];
|
||||
toWorldTransform.v6 = -toWorldMatrix4X4[6];
|
||||
toWorldTransform.v7 = toWorldMatrix4X4[7];
|
||||
toWorldTransform.v8 = toWorldMatrix4X4[8];
|
||||
toWorldTransform.v9 = toWorldMatrix4X4[9];
|
||||
toWorldTransform.v10 = -toWorldMatrix4X4[10];
|
||||
toWorldTransform.v11 = toWorldMatrix4X4[11];
|
||||
toWorldTransform.v12 = toWorldMatrix4X4[12];
|
||||
toWorldTransform.v13 = toWorldMatrix4X4[13];
|
||||
toWorldTransform.v14 = -toWorldMatrix4X4[14];
|
||||
toWorldTransform.v15 = toWorldMatrix4X4[15];
|
||||
}
|
||||
|
||||
public void SetMaterial(PXR_Audio_Spatializer_SceneMaterial material)
|
||||
{
|
||||
materialType = material.materialPreset;
|
||||
absorption.v0 = material.absorption[0];
|
||||
absorption.v1 = material.absorption[1];
|
||||
absorption.v2 = material.absorption[2];
|
||||
absorption.v3 = material.absorption[3];
|
||||
scattering = material.scattering;
|
||||
transmission = material.transmission;
|
||||
}
|
||||
|
||||
public void SetTransformMatrix4x4(Matrix4x4 toWorldMatrix4X4)
|
||||
{
|
||||
toWorldTransform.v0 = toWorldMatrix4X4[0];
|
||||
toWorldTransform.v1 = toWorldMatrix4X4[1];
|
||||
toWorldTransform.v2 = -toWorldMatrix4X4[2];
|
||||
toWorldTransform.v3 = toWorldMatrix4X4[3];
|
||||
toWorldTransform.v4 = toWorldMatrix4X4[4];
|
||||
toWorldTransform.v5 = toWorldMatrix4X4[5];
|
||||
toWorldTransform.v6 = -toWorldMatrix4X4[6];
|
||||
toWorldTransform.v7 = toWorldMatrix4X4[7];
|
||||
toWorldTransform.v8 = toWorldMatrix4X4[8];
|
||||
toWorldTransform.v9 = toWorldMatrix4X4[9];
|
||||
toWorldTransform.v10 = -toWorldMatrix4X4[10];
|
||||
toWorldTransform.v11 = toWorldMatrix4X4[11];
|
||||
toWorldTransform.v12 = toWorldMatrix4X4[12];
|
||||
toWorldTransform.v13 = toWorldMatrix4X4[13];
|
||||
toWorldTransform.v14 = -toWorldMatrix4X4[14];
|
||||
toWorldTransform.v15 = toWorldMatrix4X4[15];
|
||||
}
|
||||
}
|
||||
|
||||
enum MeshProperty : uint
|
||||
{
|
||||
Enabled = 1u,
|
||||
Material = (1u << 1),
|
||||
Absorption = (1u << 2),
|
||||
Scattering = (1u << 3),
|
||||
Transmission = (1u << 4),
|
||||
ToWorldTransform = (1u << 5),
|
||||
All = ~0u,
|
||||
None = 0u,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4fb395b406d5b459293182f1bccadd42
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user