// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections.Generic; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Audio { /// /// An audio effect that limits the frequency range of a sound to simulate being played /// over various telephony or radio sources. /// /// /// For the best results, also attach an to the sound /// source. This will ensure that the proper frequencies will be restored /// when audio influencers are used in the scene. /// [RequireComponent(typeof(AudioSource))] [RequireComponent(typeof(AudioLowPassFilter))] [RequireComponent(typeof(AudioHighPassFilter))] [DisallowMultipleComponent] [AddComponentMenu("Scripts/MRTK/SDK/AudioLoFiEffect")] public class AudioLoFiEffect : MonoBehaviour { [Tooltip("The quality level of the simulated audio source.")] [SerializeField] private AudioLoFiSourceQuality sourceQuality; /// /// The quality level of the simulated audio source (ex: AM radio). /// public AudioLoFiSourceQuality SourceQuality { get { return sourceQuality; } set { sourceQuality = value; } } /// /// The audio influencer controller that will be updated when filter settings are changed. /// [SerializeField] [HideInInspector] // The inspector will already have a reference to the object, this avoids duplication. private AudioInfluencerController influencerController = null; /// /// The audio filter settings that match the selected source quality. /// private AudioLoFiFilterSettings filterSettings; /// /// The filters used to simulate the source quality. /// private AudioLowPassFilter lowPassFilter; private AudioHighPassFilter highPassFilter; /// /// Collection used to look up the filter settings that match the selected /// source quality. /// private Dictionary sourceQualityFilterSettings = new Dictionary(); private void Awake() { influencerController = gameObject.GetComponent(); LoadQualityFilterSettings(); filterSettings = sourceQualityFilterSettings[SourceQuality]; lowPassFilter = gameObject.GetComponent(); lowPassFilter.cutoffFrequency = filterSettings.LowPassCutoff; highPassFilter = gameObject.GetComponent(); highPassFilter.cutoffFrequency = filterSettings.HighPassCutoff; } private void Update() { AudioLoFiFilterSettings newSettings = sourceQualityFilterSettings[SourceQuality]; if (newSettings != filterSettings) { // If we have an attached AudioInfluencerController, we must let it know // about our filter settings change, otherwise other effects may not behave // as expected. if (influencerController != null) { influencerController.NativeLowPassCutoffFrequency = newSettings.LowPassCutoff; influencerController.NativeHighPassCutoffFrequency = newSettings.HighPassCutoff; } filterSettings = newSettings; lowPassFilter.cutoffFrequency = filterSettings.LowPassCutoff; highPassFilter.cutoffFrequency = filterSettings.HighPassCutoff; } } /// /// Populates the source quality filter settings collection. /// private void LoadQualityFilterSettings() { if (sourceQualityFilterSettings.Keys.Count > 0) { return; } sourceQualityFilterSettings.Add( AudioLoFiSourceQuality.FullRange, new AudioLoFiFilterSettings(10, 22000)); // Frequency range: 10 Hz - 22 kHz sourceQualityFilterSettings.Add( AudioLoFiSourceQuality.NarrowBandTelephony, new AudioLoFiFilterSettings(300, 3400)); // Frequency range: 300 Hz - 3.4 kHz sourceQualityFilterSettings.Add( AudioLoFiSourceQuality.WideBandTelephony, new AudioLoFiFilterSettings(50, 7000)); // Frequency range: 50 Hz - 7 kHz sourceQualityFilterSettings.Add( AudioLoFiSourceQuality.AmRadio, new AudioLoFiFilterSettings(40, 5000)); // Frequency range: 40 Hz - 5 kHz sourceQualityFilterSettings.Add( AudioLoFiSourceQuality.FmRadio, new AudioLoFiFilterSettings(30, 15000)); // Frequency range: 30 Hz - 15 kHz } /// /// Settings for the filters used to simulate a low fidelity sound source. /// /// /// This struct is solely for the private use of the AudioLoFiEffect class. /// private struct AudioLoFiFilterSettings { /// /// The frequency below which sound will be heard. /// public float LowPassCutoff { get; } /// /// The frequency above which sound will be heard. /// public float HighPassCutoff { get; } /// /// FilterSettings constructor. /// /// High pass filter cutoff frequency. /// Low pass filter cutoff frequency. public AudioLoFiFilterSettings(float highPassCutoff, float lowPassCutoff) : this() { HighPassCutoff = highPassCutoff; LowPassCutoff = lowPassCutoff; } /// /// Checks to see if two FilterSettings objects are equivalent. /// /// True if equivalent, false otherwise. public static bool operator ==(AudioLoFiFilterSettings a, AudioLoFiFilterSettings b) { return a.Equals(b); } /// /// Checks to see if two FilterSettings objects are not equivalent. /// /// False if equivalent, true otherwise. public static bool operator !=(AudioLoFiFilterSettings a, AudioLoFiFilterSettings b) { return !(a.Equals(b)); } /// /// Checks to see if a object is equivalent to this AudioLoFiFilterSettings. /// /// True if equivalent, false otherwise. public override bool Equals(object obj) { if (obj == null) { return false; } if (!(obj is AudioLoFiFilterSettings)) { return false; } AudioLoFiFilterSettings other = (AudioLoFiFilterSettings)obj; return Equals(other); } /// /// Checks to see if a object is equivalent to this AudioLoFiFilterSettings. /// /// True if equivalent, false otherwise. public bool Equals(AudioLoFiFilterSettings other) { if ((!other.LowPassCutoff.Equals(LowPassCutoff)) || (!other.HighPassCutoff.Equals(HighPassCutoff))) { return false; } return true; } /// /// Generates a hash code representing this FilterSettings. /// public override int GetHashCode() { string s = $"[{GetType().ToString()}] Low: {LowPassCutoff}, High: {HighPassCutoff}"; return s.GetHashCode(); } } } }