// Native Audio // 5argon - Exceed7 Experiments // Problems/suggestions : 5argon@exceed7.com using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Diagnostics; using System.Linq; namespace E7.Native { /// /// Result from running . /// public class NativeAudioAnalyzerResult { public float averageFps; } /// /// The game object with this component is able to test native audio over several frames. /// public class NativeAudioAnalyzer : MonoBehaviour { /// /// You can wait for the result on this. Then after it is done, `AnalysisResult` contains the result. If not, that variable is `null`. /// /// If your game is in a yieldable routine, use `yield return new WaitUntil( () => analyzer.Analyzed );' /// /// If not, you can do a blocking wait with a `while` loop on `analyzer.Analyzed == false`. /// public bool Analyzed { get { return analyzeRoutine == null ; } } private NativeAudioAnalyzerResult analysisResult; /// /// Access this property after `Analyzed` property became true. /// public NativeAudioAnalyzerResult AnalysisResult { get { return analysisResult; } } /// /// If the analysis was too long for your liking you can reduce it here, /// but the average value return might not be so accurate. /// private const float secondsOfPlay = 1f; /// /// Assuming your game runs at 60 FPS, it will test 60 * seconds times. /// private const int framesOfPlay = (int)(60 * secondsOfPlay); private float TicksToMs(long ticks) { return ticks / 10000f; } private float TicksToMs(double ticks) { return (float)(ticks / 10000); } public List allTicks = new List(); private static float StdDev(IEnumerable values) { float ret = 0; int count = values.Count(); if (count > 1) { float avg = (float)values.Average(); float sum = values.Sum(d => (d - avg) * (d - avg)); ret = Mathf.Sqrt(sum / count); } return ret; } private static NativeAudioPointer silence; private IEnumerator analyzeRoutine; private Stopwatch sw; /// /// This is already called from /// But you can do it again if you want, it might return a new result who knows... /// /// You can wait on the public property `Analyzed` /// /// If your game is in a yieldable routine, use `yield return new WaitUntil( () => analyzer.Analyzed );' /// /// If not, you can do a blocking wait with a `while` loop on `analyzer.Analyzed == false`. /// public void Analyze() { if(analyzeRoutine != null) { StopCoroutine(analyzeRoutine); } analyzeRoutine = AnalyzeRoutine(); StartCoroutine(analyzeRoutine); } /// /// There is a test game object for running the coroutine on your scene. /// It does not take anything significant but you can call this to destroy it. /// public void Finish() { GameObject.Destroy(this); } private IEnumerator AnalyzeRoutine() { UnityEngine.Debug.Log("Built in analyze start"); sw = new Stopwatch(); allTicks = new List(); if(silence != null) { silence.Unload(); } //This "" is a special path to load a silence. silence = NativeAudio.Load(""); //To warm up the audio circuit we will discard half of the test. for (int i = 0; i < framesOfPlay/2; i++) { NativeAudio.GetNativeSourceAuto().Play(silence); yield return null; } //Ok this is the real thing. for (int i = 0; i < framesOfPlay/2; i++) { sw.Start(); NativeAudio.GetNativeSourceAuto().Play(silence); yield return null; sw.Stop(); allTicks.Add(sw.ElapsedTicks); sw.Reset(); } analysisResult = new NativeAudioAnalyzerResult(){ averageFps = 1000 / TicksToMs(allTicks.Average()) }; analyzeRoutine = null; UnityEngine.Debug.Log("Built in analyze end"); } } }