mixedreality/com.microsoft.mixedreality..../SDK/Features/Audio/Influencers/AudioOccluder.cs

114 lines
4.6 KiB
C#

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.Audio
{
/// <summary>
/// Class that implements <see cref="Microsoft.MixedReality.Toolkit.Audio.IAudioInfluencer"/> to provide an audio occlusion effect, similar
/// to listening to sound from outside of an enclosed space.
/// </summary>
/// <remarks>
/// <para>Ensure that all sound emitting objects have an attached <see cref="AudioInfluencerController"/>.
/// Failing to do so will result in the desired effect not being applied to the sound.</para>
/// </remarks>
[DisallowMultipleComponent]
[AddComponentMenu("Scripts/MRTK/SDK/AudioOccluder")]
public class AudioOccluder : MonoBehaviour, IAudioInfluencer
{
[Tooltip("Frequency above which sound will not be heard after applying occlusion.")]
[Range(10.0f, 22000.0f)]
[SerializeField]
private float cutoffFrequency = 5000.0f;
/// <summary>
/// Frequency above which sound will not be heard after applying occlusion.
/// Setting this value to 22000.0 effectively disables the effect.
/// </summary>
/// <remarks>
/// <para>Chaining occluders will result in the lowest of the cutoff frequencies being applied to the sound.
/// The CutoffFrequency range is 0.0 - 22000.0 (0 - 22kHz), inclusive.
/// The default value is 5000.0 (5kHz).</para>
/// </remarks>
public float CutoffFrequency
{
get { return cutoffFrequency; }
set
{
cutoffFrequency = Mathf.Clamp(value, 10.0f, 22000.0f);
}
}
[Tooltip("Percentage of the audio source volume that will be heard after applying occlusion.")]
[Range(0.0f, 1.0f)]
[SerializeField]
private float volumePassThrough = 1.0f;
/// <summary>
/// Percentage of the audio source volume that will be heard after applying occlusion.
/// </summary>
/// <remarks>
/// <para>VolumePassThrough is cumulative. It is applied to the current volume of the object at the time
/// the effect is applied.
/// The VolumePassThrough range is from 0.0 - 1.0 (0-100%), inclusive.
/// The default value is 1.0.</para>
/// </remarks>
public float VolumePassThrough
{
get { return volumePassThrough; }
set
{
cutoffFrequency = Mathf.Clamp(value, 0.0f, 1.0f);
}
}
// Update is not used, but is kept so that this component can be enabled/disabled.
private void Update() { }
/// <inheritdoc />
public void ApplyEffect(GameObject soundEmittingObject)
{
if (!isActiveAndEnabled)
{ return; }
AudioSource audioSource = soundEmittingObject.GetComponent<AudioSource>();
if (audioSource == null)
{
Debug.LogWarning("The specified emitter does not have an attached AudioSource component.");
return;
}
// Audio occlusion is performed using a low pass filter.
AudioLowPassFilter lowPass = soundEmittingObject.EnsureComponent<AudioLowPassFilter>();
lowPass.enabled = true;
// In the real world, chaining multiple low-pass filters will result in the
// lowest of the cutoff frequencies being the highest pitches heard.
lowPass.cutoffFrequency = Mathf.Min(lowPass.cutoffFrequency, CutoffFrequency);
// Unlike the cutoff frequency, volume pass-through is cumulative.
audioSource.volume *= VolumePassThrough;
}
/// <inheritdoc />
public void RemoveEffect(GameObject soundEmittingObject)
{
// Audio occlusion is performed using a low pass filter.
AudioLowPassFilter lowPass = soundEmittingObject.GetComponent<AudioLowPassFilter>();
if (lowPass == null) { return; }
float neutralFrequency = AudioInfluencerController.NeutralHighFrequency;
AudioInfluencerController influencerController = soundEmittingObject.GetComponent<AudioInfluencerController>();
if (influencerController != null)
{
neutralFrequency = influencerController.NativeLowPassCutoffFrequency;
}
lowPass.cutoffFrequency = neutralFrequency;
lowPass.enabled = false;
// Note: Volume attenuation is reset in the AudioInfluencerController, which is attached to the sound emitting object.
}
}
}