using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace MoreMountains.Tools { #if UNITY_EDITOR [CustomEditor(typeof(MMAudioAnalyzer), true)] [CanEditMultipleObjects] public class MMAudioAnalyzerEditor : Editor { public bool Active; public SerializedProperty BandLevels; public SerializedProperty BufferedBandLevels; public SerializedProperty NormalizedBandLevels; public SerializedProperty NormalizedBufferedBandLevels; public SerializedProperty BandPeaks; public SerializedProperty LastPeaksAt; public SerializedProperty RawSpectrum; public SerializedProperty Amplitude; public SerializedProperty NormalizedAmplitude; public SerializedProperty BufferedAmplitude; public SerializedProperty NormalizedBufferedAmplitude; public SerializedProperty PeaksPasted; public SerializedProperty Beats; // inspector protected float _inspectorWidth; protected int _numberOfBands; protected Color _barColor; protected Color _inactiveColor = new Color(0f, 0f, 0f, 0.4f); protected Color _bufferedBarColor = new Color(0f, 0f, 0f, 0.3f); protected Color _normalBarColor = MMColors.Orange; protected Color _normalNormalizedBarColor = MMColors.Aqua; protected Color _peakColor = MMColors.Yellow; protected Color _activePeakColor = Color.white; protected Color _amplitudeColor = MMColors.DarkOrange; protected Color _normalizedAmplitudeColor = MMColors.Aquamarine; protected Color _spectrumColor = MMColors.HotPink; protected Color _beatColor; protected bool _bandValuesFoldout = false; protected float _peakShowDuration = 0.5f; // box protected Vector2 _boxPosition; protected Vector2 _boxSize; protected const float _externalMargin = 12f; protected float _internalMargin = 12f; protected const float _lineHeight = 15f; protected const int _numberOfAxis = 5; protected const int _numberOfAxisSpectrum = 4; protected const int _bandsValuesBoxHeight = 150; protected const int _rawSpectrumBoxHeight = 75; // coordinates protected float _topY; protected float _boxBottomY; protected float _positionX; protected float _positionY; // column protected float _columnWidth; protected float _columnHeight; protected float _maxColumnHeight; // spectrum protected float _spectrumBoxBottomY; protected Vector2 _spectrumBoxPosition; protected Vector2 _spectrumBoxSize; protected float _spectrumMaxColumnHeight; // axis protected Vector3 _axisOrigin = Vector3.zero; protected Vector3 _axisDestination = Vector3.zero; // styles protected GUIStyle _redLabel = new GUIStyle(); protected Color _normalLabelColor; protected Rect _rect; protected virtual void OnEnable() { Active = serializedObject.FindProperty("Active").boolValue; BandLevels = serializedObject.FindProperty("BandLevels"); BufferedBandLevels = serializedObject.FindProperty("BufferedBandLevels"); NormalizedBandLevels = serializedObject.FindProperty("NormalizedBandLevels"); NormalizedBufferedBandLevels = serializedObject.FindProperty("NormalizedBufferedBandLevels"); BandPeaks = serializedObject.FindProperty("BandPeaks"); LastPeaksAt = serializedObject.FindProperty("LastPeaksAt"); RawSpectrum = serializedObject.FindProperty("RawSpectrum"); _numberOfBands = serializedObject.FindProperty("NumberOfBands").intValue; Amplitude = serializedObject.FindProperty("Amplitude"); NormalizedAmplitude = serializedObject.FindProperty("NormalizedAmplitude"); BufferedAmplitude = serializedObject.FindProperty("BufferedAmplitude"); NormalizedBufferedAmplitude = serializedObject.FindProperty("NormalizedBufferedAmplitude"); PeaksPasted = serializedObject.FindProperty("PeaksPasted"); Beats = serializedObject.FindProperty("Beats"); _redLabel.normal.textColor = Color.red; _rect = new Rect(); } /// /// Forces constant repaint of the inspector, making for much faster display of the bands bars. /// /// public override bool RequiresConstantRepaint() { return true; } public override void OnInspectorGUI() { serializedObject.Update(); UpdateNumberOfBandsIfNeeded(); DrawDefaultInspector(); _inspectorWidth = EditorGUIUtility.currentViewWidth - 24; DrawBandTable(); DrawBeats(); DrawBandVisualization(); DrawBandVisualizationNormalized(); DrawRawSpectrum(); PreProcessingButtons(); serializedObject.ApplyModifiedProperties(); } protected virtual void UpdateNumberOfBandsIfNeeded() { if (!Application.isPlaying) { _numberOfBands = serializedObject.FindProperty("NumberOfBands").intValue; } } protected virtual void DrawBandTable() { GUILayout.Space(10); GUILayout.Label("Band values", EditorStyles.boldLabel); _bandValuesFoldout = EditorGUILayout.Foldout(_bandValuesFoldout, "Levels"); if (!Active) { if (_bandValuesFoldout) { GUILayout.Label("Values are only displayed when the game is running."); } return; } if (BandPeaks.arraySize == 0) { return; } if (_bandValuesFoldout) { float win = Screen.width; float w1 = win * 0.15f; float w2 = win * 0.2f; float w3 = win * 0.2f; float w4 = win * 0.2f; float w5 = win * 0.2f; float wA = win * 0.5f; float wB = win * 0.5f; GUILayout.BeginHorizontal(); GUILayout.Label("Amplitude :", GUILayout.Width(wA)); GUILayout.Label(Amplitude.floatValue.ToString(), GUILayout.Width(wB)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Normalized Amplitude :", GUILayout.Width(wA)); GUILayout.Label(NormalizedAmplitude.floatValue.ToString(), GUILayout.Width(wB)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Buffered Amplitude :", GUILayout.Width(wA)); GUILayout.Label(BufferedAmplitude.floatValue.ToString(), GUILayout.Width(wB)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Normalized Buffered Amplitude :", GUILayout.Width(wA)); GUILayout.Label(NormalizedBufferedAmplitude.floatValue.ToString(), GUILayout.Width(wB)); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.Label("Band", EditorStyles.boldLabel, GUILayout.Width(w1)); GUILayout.Label("Value", EditorStyles.boldLabel, GUILayout.Width(w2)); GUILayout.Label("Peak", EditorStyles.boldLabel, GUILayout.Width(w3)); GUILayout.Label("Normalized", EditorStyles.boldLabel, GUILayout.Width(w4)); GUILayout.Label("Norm. Buffered", EditorStyles.boldLabel, GUILayout.Width(w5)); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); for (int i = 0; i < _numberOfBands; i++) { GUILayout.BeginHorizontal(); GUILayout.Label(i.ToString(), EditorStyles.boldLabel, GUILayout.Width(w1)); GUILayout.Label(BandLevels.GetArrayElementAtIndex(i).floatValue.ToString(), GUILayout.Width(w2)); if (Time.time - LastPeaksAt.GetArrayElementAtIndex(i).floatValue < _peakShowDuration) { _normalLabelColor = GUI.skin.label.normal.textColor; GUI.skin.label.normal.textColor = _peakColor; GUILayout.Label(BandPeaks.GetArrayElementAtIndex(i).floatValue.ToString(), GUILayout.Width(w3)); GUI.skin.label.normal.textColor = _normalLabelColor; } else { GUILayout.Label(BandPeaks.GetArrayElementAtIndex(i).floatValue.ToString(), GUILayout.Width(w3)); } GUILayout.Label(NormalizedBandLevels.GetArrayElementAtIndex(i).floatValue.ToString(), GUILayout.Width(w4)); GUILayout.Label(NormalizedBufferedBandLevels.GetArrayElementAtIndex(i).floatValue.ToString(), GUILayout.Width(w5)); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } } } protected const int _beatsBoxHeight = 40; protected virtual void DrawBeats() { if ((Beats == null) || (target as MMAudioAnalyzer).Beats == null) { return; } float length = (target as MMAudioAnalyzer).Beats.Length; if (length <= 0) { return; } float margin = _beatsBoxHeight / 10; float beatsBoxSquareSize = _beatsBoxHeight - (2 * margin); int boxesPerLine = (int)Mathf.Round((_inspectorWidth - margin - 3*_externalMargin) / (beatsBoxSquareSize + margin)) ; int numberOfLines = (int)(length / boxesPerLine) + 1; float boxHeight = (_beatsBoxHeight) * numberOfLines - margin * (numberOfLines - 1); GUILayout.Space(10); GUILayout.Label("Beats Visualization", EditorStyles.boldLabel); GUILayout.Box("", GUILayout.Width(_inspectorWidth - _externalMargin), GUILayout.Height(boxHeight)); _boxPosition = GUILayoutUtility.GetLastRect().position; _boxSize = GUILayoutUtility.GetLastRect().size; _boxBottomY = _boxPosition.y + _beatsBoxHeight - _externalMargin - _lineHeight; int counter = 0; int lineCounter = 0; for (int i = 0; i < length; i++) { if (counter > boxesPerLine - 1) { counter = 0; lineCounter++; } float boxX = _boxPosition.x + margin + counter * (beatsBoxSquareSize + margin); float boxY = _boxPosition.y + margin + lineCounter * (beatsBoxSquareSize + margin); // draw bg bar _rect.x = boxX; _rect.y = boxY; _rect.width = beatsBoxSquareSize; _rect.height = beatsBoxSquareSize; EditorGUI.DrawRect(_rect, _inactiveColor); if (Active) { // draw front bar _beatColor = (target as MMAudioAnalyzer).Beats[i].BeatColor; _beatColor.a = (target as MMAudioAnalyzer).Beats[i].CurrentValue; _rect.x = boxX; _rect.y = boxY; _rect.width = beatsBoxSquareSize; _rect.height = beatsBoxSquareSize; EditorGUI.DrawRect(_rect, _beatColor); } // draw number float labelX = (i > 9) ? boxX + beatsBoxSquareSize / 4 - 2 : boxX + beatsBoxSquareSize / 4 + 2; _rect.x = labelX; _rect.y = boxY + beatsBoxSquareSize / 4; _rect.width = beatsBoxSquareSize; _rect.height = beatsBoxSquareSize; EditorGUI.LabelField(_rect, i.ToString(), EditorStyles.boldLabel); counter++; } } protected virtual void DrawBandVisualization() { GUILayout.Space(10); GUILayout.Label("Raw Visualization", EditorStyles.boldLabel); _internalMargin = (_numberOfBands > 8) ? 6f : _externalMargin; // box GUILayout.Box("", GUILayout.Width(_inspectorWidth - _externalMargin), GUILayout.Height(_bandsValuesBoxHeight)); _boxPosition = GUILayoutUtility.GetLastRect().position; _boxSize = GUILayoutUtility.GetLastRect().size; _boxBottomY = _boxPosition.y + _boxSize.y - _externalMargin - _lineHeight; _columnWidth = (_boxSize.x - (_numberOfBands + 1) * _internalMargin) / _numberOfBands; _maxColumnHeight = _boxSize.y - 2 * _externalMargin - _lineHeight - 5; // lines Handles.BeginGUI(); // horizontal axis Handles.color = Color.grey; for (int i = 0; i < _numberOfAxis; i++) { _axisOrigin.x = _boxPosition.x; _axisOrigin.y = _boxBottomY + _lineHeight / _numberOfAxis - i * (_boxSize.y / _numberOfAxis); _axisDestination.x = _boxPosition.x + _boxSize.x; _axisDestination.y = _axisOrigin.y; Handles.DrawLine(_axisOrigin, _axisDestination); } // peaks if ((BandPeaks != null) && (BandPeaks.arraySize == _numberOfBands)) { for (int i = 0; i < _numberOfBands; i++) { float peak = BandPeaks.GetArrayElementAtIndex(i).floatValue; if (Active) { if (Time.time - LastPeaksAt.GetArrayElementAtIndex(i).floatValue < _peakShowDuration) { Handles.color = _activePeakColor; } else { Handles.color = _peakColor; } } else { Handles.color = _peakColor; } _axisOrigin.x = _boxPosition.x + _internalMargin * (i + 1) + _columnWidth * i; _axisOrigin.y = _boxBottomY - MMMaths.Remap(peak, 0f, 1f, 0f, _maxColumnHeight); _axisDestination.x = _axisOrigin.x + _columnWidth; _axisDestination.y = _axisOrigin.y; Handles.DrawLine(_axisOrigin, _axisDestination); } } Handles.EndGUI(); // amplitude cursors _columnHeight = MMMaths.Remap(Amplitude.floatValue, 0f, 1f, 0f, _maxColumnHeight); _positionX = _boxPosition.x - _externalMargin/4 ; _positionY = _boxBottomY - _columnHeight; _rect.x = _positionX; _rect.y = _positionY; _rect.width = _externalMargin / 2; _rect.height = _externalMargin / 2; EditorGUI.DrawRect(_rect, _amplitudeColor); _columnHeight = MMMaths.Remap(BufferedAmplitude.floatValue, 0f, 1f, 0f, _maxColumnHeight); _positionX = _boxPosition.x + _boxSize.x - _externalMargin / 4; _positionY = _boxBottomY - _columnHeight; _rect.x = _positionX; _rect.y = _positionY; _rect.width = _externalMargin / 2; _rect.height = _externalMargin / 2; EditorGUI.DrawRect(_rect, _amplitudeColor); // buffered bars for (int i = 0; i < _numberOfBands; i++) { if (Active) { float bandLevel = BufferedBandLevels.GetArrayElementAtIndex(i).floatValue; _columnHeight = MMMaths.Remap(bandLevel, 0f, 1f, 0f, _maxColumnHeight); _barColor = (Time.time - LastPeaksAt.GetArrayElementAtIndex(i).floatValue < _peakShowDuration/3f) ? _activePeakColor : _bufferedBarColor; _positionX = _boxPosition.x + _internalMargin * (i + 1) + _columnWidth * i; _positionY = _boxBottomY; // bar rectangle _rect.x = _positionX; _rect.y = _positionY; _rect.width = _columnWidth; _rect.height = -_columnHeight; EditorGUI.DrawRect(_rect, _barColor); } } // bars for (int i = 0; i < _numberOfBands; i++) { if (Active) { float bandLevel = BandLevels.GetArrayElementAtIndex(i).floatValue; _columnHeight = MMMaths.Remap(bandLevel, 0f, 1f, 0f, _maxColumnHeight); _barColor = (Time.time - LastPeaksAt.GetArrayElementAtIndex(i).floatValue < _peakShowDuration) ? _peakColor : _normalBarColor; } else { _barColor = _inactiveColor; _columnHeight = (i + 1) * (_maxColumnHeight / (_numberOfBands + 1)); } _positionX = _boxPosition.x + _internalMargin * (i + 1) + _columnWidth * i; _positionY = _boxBottomY; // bar rectangle _rect.x = _positionX; _rect.y = _positionY; _rect.width = _columnWidth; _rect.height = -_columnHeight; EditorGUI.DrawRect(_rect, _barColor); // bar number label float labelCorrection = (i > 9) ? -5f : 0f; _rect.x = _positionX + _columnWidth / 2 - 5 + labelCorrection; _rect.y = _boxBottomY + _lineHeight / 4; _rect.width = _columnWidth; _rect.height = _lineHeight; EditorGUI.LabelField(_rect, i.ToString(), EditorStyles.boldLabel); } } protected virtual void DrawBandVisualizationNormalized() { GUILayout.Space(10); GUILayout.Label("Normalized Visualization", EditorStyles.boldLabel); // box GUILayout.Box("", GUILayout.Width(_inspectorWidth - _externalMargin), GUILayout.Height(_bandsValuesBoxHeight)); _boxPosition = GUILayoutUtility.GetLastRect().position; _boxSize = GUILayoutUtility.GetLastRect().size; _boxBottomY = _boxPosition.y + _boxSize.y - _externalMargin - _lineHeight; _columnWidth = (_boxSize.x - (_numberOfBands + 1) * _internalMargin) / _numberOfBands; _maxColumnHeight = _boxSize.y - 2 * _externalMargin - _lineHeight; // lines Handles.BeginGUI(); // horizontal axis Handles.color = Color.grey; for (int i = 0; i < _numberOfAxis; i++) { _axisOrigin.x = _boxPosition.x; _axisOrigin.y = _boxBottomY + _lineHeight / _numberOfAxis - i * (_boxSize.y / _numberOfAxis); _axisDestination.x = _boxPosition.x + _boxSize.x; _axisDestination.y = _axisOrigin.y; Handles.DrawLine(_axisOrigin, _axisDestination); } Handles.EndGUI(); // amplitude cursors _columnHeight = MMMaths.Remap(NormalizedAmplitude.floatValue, 0f, 1f, 0f, _maxColumnHeight); _positionX = _boxPosition.x - _externalMargin / 4; _positionY = _boxBottomY - _columnHeight; _rect.x = _positionX; _rect.y = _positionY; _rect.width = _externalMargin / 2; _rect.height = _externalMargin / 2; EditorGUI.DrawRect(_rect, _normalizedAmplitudeColor); _columnHeight = MMMaths.Remap(NormalizedBufferedAmplitude.floatValue, 0f, 1f, 0f, _maxColumnHeight); _positionX = _boxPosition.x + _boxSize.x - _externalMargin / 4; _positionY = _boxBottomY - _columnHeight; _rect.x = _positionX; _rect.y = _positionY; _rect.width = _externalMargin / 2; _rect.height = _externalMargin / 2; EditorGUI.DrawRect(_rect, _normalizedAmplitudeColor); // buffered bars for (int i = 0; i < _numberOfBands; i++) { if (Active) { float bandLevel = NormalizedBufferedBandLevels.GetArrayElementAtIndex(i).floatValue; _columnHeight = MMMaths.Remap(bandLevel, 0f, 1f, 0f, _maxColumnHeight); _barColor = (Time.time - LastPeaksAt.GetArrayElementAtIndex(i).floatValue < _peakShowDuration / 3f) ? _activePeakColor : _bufferedBarColor; _positionX = _boxPosition.x + _internalMargin * (i + 1) + _columnWidth * i; _positionY = _boxBottomY; // bar rectangle _rect.x = _positionX; _rect.y = _positionY; _rect.width = _columnWidth; _rect.height = -_columnHeight; EditorGUI.DrawRect(_rect, _barColor); } } // bars for (int i = 0; i < _numberOfBands; i++) { if (Active) { float bandLevel = NormalizedBandLevels.GetArrayElementAtIndex(i).floatValue; _columnHeight = MMMaths.Remap(bandLevel, 0f, 1f, 0f, _maxColumnHeight); _barColor = (Time.time - LastPeaksAt.GetArrayElementAtIndex(i).floatValue < _peakShowDuration) ? _peakColor : _normalNormalizedBarColor; } else { _barColor = _inactiveColor; _columnHeight = (i + 1) * (_maxColumnHeight / (_numberOfBands + 1)); } _positionX = _boxPosition.x + _internalMargin * (i + 1) + _columnWidth * i; _positionY = _boxBottomY; // bar rectangle _rect.x = _positionX; _rect.y = _positionY; _rect.width = _columnWidth; _rect.height = -_columnHeight; EditorGUI.DrawRect(_rect, _barColor); // bar number label float labelCorrection = (i > 9) ? -5f : 0f; _rect.x = _positionX + _columnWidth / 2 - 5 + labelCorrection; _rect.y = _boxBottomY + _lineHeight / 4; _rect.width = _columnWidth; _rect.height = _lineHeight; EditorGUI.LabelField(_rect, i.ToString(), EditorStyles.boldLabel); } } protected virtual void DrawRawSpectrum() { GUILayout.Space(10); GUILayout.Label("Raw Spectrum", EditorStyles.boldLabel); // box GUILayout.Box("", GUILayout.Width(_inspectorWidth - _externalMargin), GUILayout.Height(_rawSpectrumBoxHeight)); _spectrumBoxPosition = GUILayoutUtility.GetLastRect().position; _spectrumBoxSize = GUILayoutUtility.GetLastRect().size; _spectrumBoxBottomY = _spectrumBoxPosition.y + _spectrumBoxSize.y; _spectrumMaxColumnHeight = _spectrumBoxSize.y - 2 * _externalMargin; Handles.BeginGUI(); // horizontal axis Handles.color = Color.grey; for (int i = 0; i < _numberOfAxisSpectrum; i++) { _axisOrigin.x = _spectrumBoxPosition.x; _axisOrigin.y = _spectrumBoxBottomY - i * (_spectrumBoxSize.y / _numberOfAxisSpectrum); _axisDestination.x = _spectrumBoxPosition.x + _spectrumBoxSize.x; _axisDestination.y = _axisOrigin.y; Handles.DrawLine(_axisOrigin, _axisDestination); } if (Active) { // spectrum for (int i = 1; i < RawSpectrum.arraySize - 1; i++) { float xPosition = _spectrumBoxPosition.x + _externalMargin + MMMaths.Remap(i, 0, RawSpectrum.arraySize, 0f, _spectrumBoxSize.x - _externalMargin * 2); float yPosition = _spectrumBoxPosition.y + _spectrumBoxSize.y / 2 ; float deltaX = (_spectrumBoxSize.x - _externalMargin * 2) / RawSpectrum.arraySize; float spectrumValue = RawSpectrum.GetArrayElementAtIndex(i).floatValue; float spectrumValuePrevious = RawSpectrum.GetArrayElementAtIndex(i - 1).floatValue; float factor = _spectrumBoxSize.y/2; spectrumValue = - (1 / Mathf.Log(spectrumValue)) * factor; ; spectrumValuePrevious = - (1 / Mathf.Log(spectrumValuePrevious)) * factor; spectrumValue = Mathf.Clamp(spectrumValue, 0f, _spectrumBoxSize.y / 2f); spectrumValuePrevious = Mathf.Clamp(spectrumValuePrevious, 0f, _spectrumBoxSize.y / 2f); Handles.color = _spectrumColor; _axisOrigin.x = xPosition - deltaX; _axisOrigin.y = yPosition + spectrumValuePrevious; _axisDestination.x = xPosition; _axisDestination.y = (i % 2 == 0) ? yPosition + spectrumValue : yPosition - spectrumValue; Handles.DrawLine(_axisOrigin, _axisDestination); } } else { int points = 100; for (int i = 1; i < points - 1; i++) { float xPosition = _spectrumBoxPosition.x + _externalMargin + MMMaths.Remap(i, 0, points, 0f, _spectrumBoxSize.x - _externalMargin * 2); float yPosition = _spectrumBoxPosition.y + _spectrumBoxSize.y / 2; float deltaBetweenXandXPrevious = (_spectrumBoxSize.x - _externalMargin * 2) / points; float spectrumValue = Foobar(i); float spectrumValuePrevious = Foobar(i-1); float factor = _spectrumBoxSize.y / 2; Handles.color = _inactiveColor; _axisOrigin.x = xPosition - deltaBetweenXandXPrevious; _axisOrigin.y = yPosition + spectrumValuePrevious; _axisDestination.x = xPosition; _axisDestination.y = yPosition + spectrumValue ; var p1 = _axisOrigin; var p2 = _axisDestination; var thickness = 3; Handles.DrawBezier(p1, p2, p1, p2, _spectrumColor, null, thickness); } } Handles.EndGUI(); } protected virtual float Foobar(float x) { return 25f * Mathf.Sin(x * 0.5f); } protected virtual void PreProcessingButtons() { if ((target as MMAudioAnalyzer).Mode != MMAudioAnalyzer.Modes.AudioSource) { return; } GUILayout.Space(10); GUILayout.Label("Peaks preprocessing", EditorStyles.boldLabel); if (!PeaksPasted.boolValue) { if ((PeaksSaver.Peaks == null) || (PeaksSaver.Peaks.Length == 0)) { EditorGUILayout.HelpBox("You haven't preprocessed peaks for this track yet. It's recommended to do so, by pressing play, " + "then the Find Peaks button below. Then, exit play, and press the 'Paste Peaks' button.", MessageType.Warning); if (GUILayout.Button("Find Peaks")) { (target as MMAudioAnalyzer).FindPeaks(); } } else { EditorGUILayout.HelpBox("Exit Play Mode first, then paste your saved peaks using the 'Paste Peaks' button below.", MessageType.Warning); if (!Application.isPlaying) { if (GUILayout.Button("Paste Peaks")) { (target as MMAudioAnalyzer).PastePeaks(); } } } } if (GUILayout.Button("Clear Peaks")) { (target as MMAudioAnalyzer).ClearPeaks(); } } } #endif }