654 lines
20 KiB
C#
654 lines
20 KiB
C#
// 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.");
|
|
}
|
|
} |