void DrawLPCSpectralEnvelope() { var origColor = Handles.color; var area = GUILayoutUtility.GetRect(Screen.width, 300f); area = EditorGUI.IndentedRect(area); var margin = new EditorUtil.Margin(10, 10f, 30f, 40f); var range = new Vector2(lipSync.maxFreq, 1f); EditorUtil.DrawGrid( area, Color.white, new Color(1f, 1f, 1f, 0.5f), margin, range, new Vector2(8f, 1f)); if (!Application.isPlaying) { return; } float xMin = area.x + margin.left; float xMax = area.xMax - margin.right; float yMin = area.y + margin.top; float yMax = area.yMax - margin.bottom; float width = xMax - xMin; float height = yMax - yMin; var H = lipSync.lpcSpectralEnvelopeForEditorOnly; float df = (float)lipSync.maxFreq / H.Length; float nf = range.x / df; int n = (int)nf; if (showFft) { var spectrum = lipSync.fftDataEditor; float maxSpectrum = Algorithm.GetMaxValue(ref spectrum); float dfFft = (float)AudioSettings.outputSampleRate / spectrum.Length; float nfFft = range.x / dfFft; int nFft = (int)nfFft; var pointsFft = new Vector3[nFft]; float min = Mathf.Log10(1e-3f); for (int i = 0; i < nFft && i < spectrum.Length; ++i) { float val = spectrum[i] / maxSpectrum; val = Mathf.Log10(10f * val); val = (val - min) / (1f - min); val = Mathf.Max(val, 0f); float x = xMin + width * dfFft * i / lipSync.maxFreq; float y = yMax - height * val; pointsFft[i] = new Vector3(x, y, 0f); } Handles.color = Color.gray; Handles.DrawAAPolyLine(3f, pointsFft); } if (showDLpc) { var ddH = lipSync.ddLpcSpectralEnvelopeForEditorOnly; float maxDdH = Algorithm.GetMaxValue(ref ddH); var points = new Vector3[n]; float min = Mathf.Log10(1e-8f); for (int i = 0; i < n && i < ddH.Length; ++i) { float val = ddH[i] / maxDdH; val = Mathf.Log10(10f * val); val = (val - min) / (1f - min); val = Mathf.Max(val, 0f); float x = xMin + width * df * i / lipSync.maxFreq; float y = yMax - height * val; points[i] = new Vector3(x, y, 0f); } Handles.color = new Color(0f, 0f, 1f, 0.2f); Handles.DrawAAPolyLine(3f, points); } if (showLpc) { float maxH = Algorithm.GetMaxValue(ref H); var points = new Vector3[n]; float min = Mathf.Log10(1e-2f); for (int i = 0; i < n && i < H.Length; ++i) { float val = H[i] / maxH; val = Mathf.Log10(10f * val); val = (val - min) / (1f - min); val = Mathf.Max(val, 0f); float x = xMin + width * df * i / lipSync.maxFreq; float y = yMax - height * val; points[i] = new Vector3(x, y, 0f); } Handles.color = Color.red; Handles.DrawAAPolyLine(5f, points); } if (showFormant) { var result = lipSync.result; float xF1 = xMin + width * (result.formant.f1 / df) / n; float xF2 = xMin + width * (result.formant.f2 / df) / n; var pointsF1 = new Vector3[2] { new Vector3(xF1, yMin, 0f), new Vector3(xF1, yMax, 0f) }; var pointsF2 = new Vector3[2] { new Vector3(xF2, yMin, 0f), new Vector3(xF2, yMax, 0f) }; Handles.color = Color.green; Handles.DrawAAPolyLine(3f, pointsF1); Handles.color = Color.blue; Handles.DrawAAPolyLine(3f, pointsF2); EditorGUI.LabelField(new Rect(xF1 + 10f, yMin + 10f, 200f, 20f), $"f1: {(int)result.formant.f1} Hz"); EditorGUI.LabelField(new Rect(xF2 + 10f, yMin + 30f, 200f, 20f), $"f2: {(int)result.formant.f2} Hz"); } EditorGUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); showLpc = EditorGUILayout.ToggleLeft("LPC", showLpc, GUILayout.MaxWidth(100)); showDLpc = EditorGUILayout.ToggleLeft("dLPC", showDLpc, GUILayout.MaxWidth(100)); showFft = EditorGUILayout.ToggleLeft("FFT", showFft, GUILayout.MaxWidth(100)); showFormant = EditorGUILayout.ToggleLeft("Formant", showFormant, GUILayout.MaxWidth(100)); GUILayout.FlexibleSpace(); EditorGUILayout.EndHorizontal(); Handles.color = origColor; }