// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Audio { /// /// Class that implements to provide an audio occlusion effect, similar /// to listening to sound from outside of an enclosed space. /// /// /// Ensure that all sound emitting objects have an attached . /// Failing to do so will result in the desired effect not being applied to the sound. /// [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; /// /// Frequency above which sound will not be heard after applying occlusion. /// Setting this value to 22000.0 effectively disables the effect. /// /// /// 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). /// 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; /// /// Percentage of the audio source volume that will be heard after applying occlusion. /// /// /// 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. /// 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() { } /// public void ApplyEffect(GameObject soundEmittingObject) { if (!isActiveAndEnabled) { return; } AudioSource audioSource = soundEmittingObject.GetComponent(); 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(); 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; } /// public void RemoveEffect(GameObject soundEmittingObject) { // Audio occlusion is performed using a low pass filter. AudioLowPassFilter lowPass = soundEmittingObject.GetComponent(); if (lowPass == null) { return; } float neutralFrequency = AudioInfluencerController.NeutralHighFrequency; AudioInfluencerController influencerController = soundEmittingObject.GetComponent(); 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. } } }