407 lines
10 KiB
C#
407 lines
10 KiB
C#
/************************************************************************************
|
|
Filename : ONSPAudioSource.cs
|
|
Content : Interface into the Oculus Native Spatializer Plugin
|
|
Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved.
|
|
|
|
Licensed under the Oculus SDK Version 3.5 (the "License");
|
|
you may not use the Oculus SDK except in compliance with the License,
|
|
which is provided at the time of installation or download, or which
|
|
otherwise accompanies this software in either electronic or hard copy form.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
https://developer.oculus.com/licenses/sdk-3.5/
|
|
|
|
Unless required by applicable law or agreed to in writing, the Oculus SDK
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
************************************************************************************/
|
|
|
|
// Uncomment below to test access of read-only spatializer parameters
|
|
//#define TEST_READONLY_PARAMETERS
|
|
|
|
using UnityEngine;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Runtime.InteropServices;
|
|
|
|
public class ONSPAudioSource : MonoBehaviour
|
|
{
|
|
#if TEST_READONLY_PARAMETERS
|
|
// Spatializer read-only system parameters (global)
|
|
static int readOnly_GlobalRelectionOn = 8;
|
|
static int readOnly_NumberOfUsedSpatializedVoices = 9;
|
|
#endif
|
|
|
|
#if VRC_CLIENT
|
|
// don't include in SDK, not compatible with Unity's version of spatializer
|
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
|
static void OnBeforeSceneLoadRuntimeMethod()
|
|
{
|
|
OSP_SetGlobalVoiceLimit(ONSPSettings.Instance.voiceLimit);
|
|
}
|
|
#endif
|
|
|
|
// Import functions
|
|
public const string strONSPS = "AudioPluginOculusSpatializer";
|
|
|
|
[DllImport(strONSPS)]
|
|
private static extern void ONSP_GetGlobalRoomReflectionValues(ref bool reflOn, ref bool reverbOn,
|
|
ref float width, ref float height, ref float length);
|
|
|
|
// Public
|
|
|
|
[SerializeField]
|
|
private bool enableSpatialization = true;
|
|
public bool EnableSpatialization
|
|
{
|
|
get
|
|
{
|
|
return enableSpatialization;
|
|
}
|
|
set
|
|
{
|
|
enableSpatialization = value;
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private float gain = 0.0f;
|
|
public float Gain
|
|
{
|
|
get
|
|
{
|
|
return gain;
|
|
}
|
|
set
|
|
{
|
|
gain = Mathf.Clamp(value, 0.0f, 24.0f);
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private bool useInvSqr = false;
|
|
public bool UseInvSqr
|
|
{
|
|
get
|
|
{
|
|
return useInvSqr;
|
|
}
|
|
set
|
|
{
|
|
useInvSqr = value;
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private float near = 0.25f;
|
|
public float Near
|
|
{
|
|
get
|
|
{
|
|
return near;
|
|
}
|
|
set
|
|
{
|
|
near = Mathf.Clamp(value, 0.0f, 1000000.0f);
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private float far = 250.0f;
|
|
public float Far
|
|
{
|
|
get
|
|
{
|
|
return far;
|
|
}
|
|
set
|
|
{
|
|
far = Mathf.Clamp(value, 0.0f, 1000000.0f);
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private float volumetricRadius = 0.0f;
|
|
public float VolumetricRadius
|
|
{
|
|
get
|
|
{
|
|
return volumetricRadius;
|
|
}
|
|
set
|
|
{
|
|
volumetricRadius = Mathf.Clamp(value, 0.0f, 1000.0f);
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private float reverbSend = 0.0f;
|
|
public float ReverbSend
|
|
{
|
|
get
|
|
{
|
|
return reverbSend;
|
|
}
|
|
set
|
|
{
|
|
reverbSend = Mathf.Clamp(value, -60.0f, 20.0f);
|
|
}
|
|
}
|
|
|
|
|
|
[SerializeField]
|
|
private bool enableRfl = false;
|
|
public bool EnableRfl
|
|
{
|
|
get
|
|
{
|
|
return enableRfl;
|
|
}
|
|
set
|
|
{
|
|
enableRfl = value;
|
|
}
|
|
}
|
|
|
|
|
|
/// VRCHAT: We need to reset the params after avatar loading and viseme setup
|
|
public void Reset()
|
|
{
|
|
var source = GetComponent<AudioSource>();
|
|
|
|
if(source == null)
|
|
{
|
|
enabled = false;
|
|
return;
|
|
}
|
|
|
|
SetParameters(ref source);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Awake this instance.
|
|
/// </summary>
|
|
void Awake()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start this instance.
|
|
/// </summary>
|
|
void Start()
|
|
{
|
|
#if VRC_CLIENT
|
|
var source = GetComponent<AudioSource>();
|
|
|
|
if(source != null && source.outputAudioMixerGroup != VRCAudioManager.GetAvatarGroup())
|
|
VRCAudioManager.ApplyGameAudioMixerSettings(source);
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update this instance.
|
|
/// </summary>
|
|
void LateUpdate()
|
|
{
|
|
// We might iterate through multiple sources / game object
|
|
var source = GetComponent<AudioSource>();
|
|
|
|
if(source == null)
|
|
{
|
|
enabled = false;
|
|
return;
|
|
}
|
|
|
|
// READ-ONLY PARAMETER TEST
|
|
#if TEST_READONLY_PARAMETERS
|
|
float rfl_enabled = 0.0f;
|
|
source.GetSpatializerFloat(readOnly_GlobalRelectionOn, out rfl_enabled);
|
|
float num_voices = 0.0f;
|
|
source.GetSpatializerFloat(readOnly_NumberOfUsedSpatializedVoices, out num_voices);
|
|
|
|
String readOnly = System.String.Format
|
|
("Read only values: refl enabled: {0:F0} num voices: {1:F0}", rfl_enabled, num_voices);
|
|
Debug.Log(readOnly);
|
|
#endif
|
|
|
|
// Check to see if we should disable spatializion
|
|
if ((Application.isPlaying == false) ||
|
|
(AudioListener.pause == true) ||
|
|
(source.isPlaying == false) ||
|
|
(source.isActiveAndEnabled == false)
|
|
)
|
|
{
|
|
source.spatialize = false;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
SetParameters(ref source);
|
|
}
|
|
}
|
|
|
|
enum Parameters : int
|
|
{
|
|
P_GAIN = 0,
|
|
P_USEINVSQR,
|
|
P_NEAR,
|
|
P_FAR,
|
|
P_RADIUS,
|
|
P_DISABLE_RFL,
|
|
P_VSPEAKERMODE,
|
|
P_AMBISTAT,
|
|
P_READONLY_GLOBAL_RFL_ENABLED, // READ-ONLY
|
|
P_READONLY_NUM_VOICES, // READ-ONLY
|
|
P_SENDLEVEL,
|
|
P_NUM
|
|
};
|
|
|
|
/// <summary>
|
|
/// Sets the parameters.
|
|
/// </summary>
|
|
/// <param name="source">Source.</param>
|
|
public void SetParameters(ref AudioSource source)
|
|
{
|
|
// VRCHAT: indentation is weird intentionally, for easier diff
|
|
// VRC jnuccio: added try here to catch unknown exception reported in analytics
|
|
try
|
|
{
|
|
if (source == null)
|
|
return;
|
|
|
|
// See if we should enable spatialization
|
|
source.spatialize = enableSpatialization;
|
|
|
|
source.SetSpatializerFloat((int)Parameters.P_GAIN, gain);
|
|
// All inputs are floats; convert bool to 0.0 and 1.0
|
|
if(useInvSqr == true)
|
|
source.SetSpatializerFloat((int)Parameters.P_USEINVSQR, 1.0f);
|
|
else
|
|
source.SetSpatializerFloat((int)Parameters.P_USEINVSQR, 0.0f);
|
|
|
|
source.SetSpatializerFloat((int)Parameters.P_NEAR, near);
|
|
source.SetSpatializerFloat((int)Parameters.P_FAR, far);
|
|
|
|
source.SetSpatializerFloat((int)Parameters.P_RADIUS, volumetricRadius);
|
|
|
|
if(enableRfl == true)
|
|
source.SetSpatializerFloat((int)Parameters.P_DISABLE_RFL, 0.0f);
|
|
else
|
|
source.SetSpatializerFloat((int)Parameters.P_DISABLE_RFL, 1.0f);
|
|
|
|
source.SetSpatializerFloat((int)Parameters.P_SENDLEVEL, reverbSend);
|
|
|
|
// VRCHAT: indentation is weird intentionally, for easier diff
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// not sure why this throws sometimes
|
|
}
|
|
}
|
|
|
|
private static ONSPAudioSource RoomReflectionGizmoAS = null;
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
void OnDrawGizmos()
|
|
{
|
|
// Are we the first one created? make sure to set our static ONSPAudioSource
|
|
// for drawing out room parameters once
|
|
if(RoomReflectionGizmoAS == null)
|
|
{
|
|
RoomReflectionGizmoAS = this;
|
|
}
|
|
|
|
Color c;
|
|
const float colorSolidAlpha = 0.1f;
|
|
|
|
// Draw the near/far spheres
|
|
|
|
// Near (orange)
|
|
c.r = 1.0f;
|
|
c.g = 0.5f;
|
|
c.b = 0.0f;
|
|
c.a = 1.0f;
|
|
Gizmos.color = c;
|
|
Gizmos.DrawWireSphere(transform.position, Near);
|
|
c.a = colorSolidAlpha;
|
|
Gizmos.color = c;
|
|
Gizmos.DrawSphere(transform.position, Near);
|
|
|
|
// Far (red)
|
|
c.r = 1.0f;
|
|
c.g = 0.0f;
|
|
c.b = 0.0f;
|
|
c.a = 1.0f;
|
|
Gizmos.color = Color.red;
|
|
Gizmos.DrawWireSphere(transform.position, Far);
|
|
c.a = colorSolidAlpha;
|
|
Gizmos.color = c;
|
|
Gizmos.DrawSphere(transform.position, Far);
|
|
|
|
// 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, VolumetricRadius);
|
|
c.a = colorSolidAlpha;
|
|
Gizmos.color = c;
|
|
Gizmos.DrawSphere(transform.position, VolumetricRadius);
|
|
|
|
// Draw room parameters ONCE only, provided reflection engine is on
|
|
if (RoomReflectionGizmoAS == this)
|
|
{
|
|
// Get global room parameters (write new C api to get reflection values)
|
|
bool reflOn = false;
|
|
bool reverbOn = false;
|
|
float width = 1.0f;
|
|
float height = 1.0f;
|
|
float length = 1.0f;
|
|
|
|
ONSP_GetGlobalRoomReflectionValues(ref reflOn, ref reverbOn, ref width, ref height, ref length);
|
|
|
|
// TO DO: Get the room reflection values and render those out as well (like we do in the VST)
|
|
|
|
if((Camera.main != null) && (reflOn == true))
|
|
{
|
|
// Set color of cube (cyan is early reflections only, white is with reverb on)
|
|
if(reverbOn == true)
|
|
c = Color.white;
|
|
else
|
|
c = Color.cyan;
|
|
|
|
Gizmos.color = c;
|
|
Gizmos.DrawWireCube(Camera.main.transform.position, new Vector3(width, height, length));
|
|
c.a = colorSolidAlpha;
|
|
Gizmos.color = c;
|
|
Gizmos.DrawCube(Camera.main.transform.position, new Vector3(width, height, length));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
void OnDestroy()
|
|
{
|
|
// We will null out single pointer instance
|
|
// of the room reflection gizmo since we are being destroyed.
|
|
// Any ONSPAS that is alive or born will re-set this pointer
|
|
// so that we only draw it once
|
|
if(RoomReflectionGizmoAS == this)
|
|
{
|
|
RoomReflectionGizmoAS = null;
|
|
}
|
|
}
|
|
|
|
[System.Runtime.InteropServices.DllImport("AudioPluginOculusSpatializer")]
|
|
private static extern int OSP_SetGlobalVoiceLimit(int VoiceLimit);
|
|
}
|