KaiClavier

AudioClipPreviewPropertyDrawer.cs

Sep 21st, 2021 (edited)
704
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. using UnityEngine;
  2.  
  3. #if UNITY_EDITOR
  4. using UnityEditor;
  5. using System.Reflection;
  6. using System;
  7. #endif
  8.  
  9. namespace Clavian
  10. {
  11.     /*
  12.     audio stop/play code from https://forum.unity.com/threads/reflected-audioutil-class-for-making-audio-based-editor-extensions.308133/
  13.  
  14.     waveform drawing adapted from https://answers.unity.com/questions/1603418/how-to-create-waveform-texture-from-audioclip.html
  15.     //...but this ended up being unused. going to keep it here just in case it turns out to be more efficient. I'll keep it in here just to keep everything in one place!
  16.  
  17.     for Unity 2020, apparently some of the classes have changed: https://forum.unity.com/threads/reflected-audioutil-class-for-making-audio-based-editor-extensions.308133/#post-6459664
  18.     //applied this but havent actually tested in unity 2020.2 yet.
  19.  
  20.     I also found this when I was 99% done. helped me with making just one button update in the inspector tho.
  21.     https://www.reddit.com/r/Unity3D/comments/5n6ddx/audioclip_propertydrawer/
  22.     */
  23.    
  24.     /*
  25.     //attribute, in case this code shouldn't apply to all audio clips. [AudioClipPreview]
  26.     public class AudioClipPreview : PropertyAttribute
  27.     {
  28.         public AudioClipPreview()
  29.         {
  30.  
  31.         }
  32.     }
  33.     */
  34.     #if UNITY_EDITOR
  35.     //in case an attribute should be used instead:
  36.     //[CustomPropertyDrawer(typeof(AudioClipPreview))]
  37.     //attribute no longer needed if i just make this work with all audioclips.
  38.     [CustomPropertyDrawer(typeof(AudioClip))]
  39.     public class AudioClipDrawer : PropertyDrawer
  40.     {
  41.  
  42.         private static string currentClipName;
  43.         private bool isThisClipPlaying = false;
  44.         private Rect fieldRect;
  45.         private AudioClip targetClip;
  46.  
  47.         public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
  48.         {
  49.  
  50.             EditorGUI.BeginProperty(position, label, property);
  51.  
  52.             var target = property.objectReferenceValue;
  53.             //label
  54.             position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
  55.  
  56.             //rects
  57.             var buttonRect = new Rect(position.x, position.y, 20, position.height);
  58.             fieldRect = new Rect(position.x + 25, position.y, position.width - 25, position.height);
  59.  
  60.             if(target == null) //object field
  61.             {
  62.                 EditorGUI.ObjectField(fieldRect, property, GUIContent.none);
  63.             }
  64.             else if(target.GetType() != typeof(AudioClip))
  65.             {
  66.                 //hey wrong type!
  67.                 EditorGUI.LabelField(fieldRect, "Wrong type! Must be AudioClip.");
  68.             }
  69.             else
  70.             {
  71.                 targetClip = (AudioClip)target;
  72.                 isThisClipPlaying = EditorSFX.IsClipPlaying(targetClip) && (property.propertyPath == currentClipName);
  73.                 //draw fields
  74.                 if(isThisClipPlaying)
  75.                 {
  76.                     if(GUI.Button(buttonRect, "■"))
  77.                     {
  78.                         currentClipName = string.Empty;
  79.                         EditorSFX.StopAllClips();
  80.                     }
  81.                 }
  82.                 else
  83.                 {
  84.                     if(GUI.Button(buttonRect, "▶"))
  85.                     {
  86.                         currentClipName = property.propertyPath;
  87.                         //Debug.Log(target.GetType().ToString() + " " + target.name);
  88.                         //play that sound.
  89.                         EditorSFX.StopAllClips();
  90.                         EditorSFX.PlayClip(targetClip, 0, false);
  91.                     }
  92.                 }
  93.                 //EditorGUI.PropertyField(fieldRect, property.FindPropertyRelative)
  94.                 EditorGUI.ObjectField(fieldRect, property, typeof(AudioClip), GUIContent.none);
  95.                 var myGUIStyle = new GUIStyle(GUIStyle.none);
  96.                 //THIS ONE WORKS: but
  97.             //  var waveformTex = EditorSFX.PaintWaveformSpectrum(targetClip, 1f, 100, 20, new Color(1f, 0.55f, 0f, 0.3f), Color.clear);
  98.                 var waveformTex = AssetPreview.GetAssetPreview(property.objectReferenceValue);
  99.                 GUI.color = new Color(1f,1f,1f,0.3f);
  100.                 myGUIStyle.normal.background = waveformTex;
  101.                 EditorGUI.LabelField(fieldRect, GUIContent.none, myGUIStyle);
  102.                 GUI.color = Color.white;
  103.  
  104.                 if(isThisClipPlaying)
  105.                 {
  106.                     //draw progress bar.
  107.                    
  108.                     var samplerRect = new Rect(fieldRect.x + ((float)EditorSFX.GetClipSamplePosition(targetClip) / (float)targetClip.samples * fieldRect.width), fieldRect.y, 1f, fieldRect.height);
  109.                     var samplerGUIStyle = new GUIStyle(GUIStyle.none);
  110.                     samplerGUIStyle.normal.background = Texture2D.whiteTexture;
  111.                     EditorGUI.LabelField(samplerRect, GUIContent.none, samplerGUIStyle);
  112.                 }
  113.                
  114.             }
  115.  
  116.             EditorGUI.EndProperty();
  117.         }
  118.     }
  119.  
  120.     public static class EditorSFX
  121.     {
  122.    
  123.         public static void PlayClip(AudioClip clip , int startSample , bool loop) {
  124.             Assembly unityEditorAssembly = typeof(AudioImporter).Assembly;
  125.             Type audioUtilClass = unityEditorAssembly.GetType("UnityEditor.AudioUtil");
  126.             MethodInfo method = audioUtilClass.GetMethod(
  127.                 #if UNITY_2020_2_OR_NEWER
  128.                 "PlayPreviewClip"
  129.                 #else
  130.                 "PlayClip",
  131.                 #endif
  132.                 BindingFlags.Static | BindingFlags.Public,
  133.                 null,
  134.                 new System.Type[] {
  135.                 typeof(AudioClip),
  136.                 typeof(Int32),
  137.                 typeof(Boolean)
  138.             },
  139.             null
  140.             );
  141.             method.Invoke(
  142.                 null,
  143.                 new object[] {
  144.                 clip,
  145.                 startSample,
  146.                 loop
  147.             }
  148.             );
  149.         }
  150.    
  151.         public static void StopAllClips () {
  152.             Assembly unityEditorAssembly = typeof(AudioImporter).Assembly;
  153.             Type audioUtilClass = unityEditorAssembly.GetType("UnityEditor.AudioUtil");
  154.             MethodInfo method = audioUtilClass.GetMethod(
  155.                 #if UNITY_2020_2_OR_NEWER
  156.                 "StopAllPreviewClips",
  157.                 #else
  158.                 "StopAllClips",
  159.                 #endif
  160.                 BindingFlags.Static | BindingFlags.Public
  161.                 );
  162.  
  163.             method.Invoke(
  164.                 null,
  165.                 null
  166.                 );
  167.         }
  168.  
  169.         public static bool IsClipPlaying(AudioClip clip) {
  170.             Assembly unityEditorAssembly = typeof(AudioImporter).Assembly;
  171.             Type audioUtilClass = unityEditorAssembly.GetType("UnityEditor.AudioUtil");
  172.             MethodInfo method = audioUtilClass.GetMethod(
  173.                 #if UNITY_2020_2_OR_NEWER
  174.                 "IsPreviewClipPlaying",
  175.                 #else
  176.                 "IsClipPlaying",
  177.                 #endif
  178.                 BindingFlags.Static | BindingFlags.Public
  179.                 );
  180.            
  181.             bool playing = (bool)method.Invoke(
  182.                 null,
  183.                 new object[] {
  184.                 #if UNITY_2020_2_OR_NEWER
  185.                 #else
  186.                 clip,
  187.                 #endif
  188.             }
  189.             );
  190.            
  191.             return playing;
  192.         }
  193.  
  194.         public static float GetClipPosition(AudioClip clip) {
  195.             Assembly unityEditorAssembly = typeof(AudioImporter).Assembly;
  196.             Type audioUtilClass = unityEditorAssembly.GetType("UnityEditor.AudioUtil");
  197.             MethodInfo method = audioUtilClass.GetMethod(
  198.                 "GetClipPosition",
  199.                 BindingFlags.Static | BindingFlags.Public
  200.                 );
  201.            
  202.             float position = (float)method.Invoke(
  203.                 null,
  204.                 new object[] {
  205.                 clip
  206.             }
  207.             );
  208.            
  209.             return position;
  210.         }
  211.  
  212.         public static int GetClipSamplePosition(AudioClip clip) {
  213.             Assembly unityEditorAssembly = typeof(AudioImporter).Assembly;
  214.             Type audioUtilClass = unityEditorAssembly.GetType("UnityEditor.AudioUtil");
  215.             MethodInfo method = audioUtilClass.GetMethod(
  216.                 "GetClipSamplePosition",
  217.                 BindingFlags.Static | BindingFlags.Public
  218.                 );
  219.            
  220.             int position = (int)method.Invoke(
  221.                 null,
  222.                 new object[] {
  223.                 #if UNITY_2020_2_OR_NEWER
  224.                 #else
  225.                 clip
  226.                 #endif
  227.             }
  228.             );
  229.            
  230.             return position;
  231.         }
  232. /*
  233.         public static Texture2D PaintWaveformSpectrum(AudioClip audio, float saturation, int width, int height, Color col, Color bgCol) {
  234.             Texture2D tex = new Texture2D(width, height, TextureFormat.RGBA32, false);
  235.             if(audio.loadType != AudioClipLoadType.DecompressOnLoad)
  236.             {
  237.                 //unable to render.
  238.  
  239.                 for (int x = 0; x < width; x++) {
  240.                     for (int y = 0; y < height; y++) {
  241.                         tex.SetPixel(x, y, bgCol);
  242.                     }
  243.                 }
  244.  
  245.                 tex.Apply();
  246.             }
  247.             else
  248.             {
  249.                 float[] samples = new float[audio.samples * audio.channels];
  250.                 float[] waveform = new float[width];
  251.  
  252.                 audio.GetData(samples, 0);
  253.                 float packSize = ((float)samples.Length / (float)width);
  254.                 int s = 0;
  255.                 for (float i = 0; Mathf.RoundToInt(i) < samples.Length && s < waveform.Length; i += packSize)
  256.                 {
  257.                     waveform[s] = Mathf.Abs(samples[Mathf.RoundToInt(i)]);
  258.                     s++;
  259.                 }
  260.            
  261.                 for (int x = 0; x < width; x++) {
  262.                     for (int y = 0; y < height; y++) {
  263.                         tex.SetPixel(x, y, bgCol);
  264.                     }
  265.                 }
  266.            
  267.                 for (int x = 0; x < waveform.Length; x++) {
  268.                     for (int y = 0; y <= waveform[x] * ((float)height * .75f); y++) {
  269.                         tex.SetPixel(x, ( height / 2 ) + y, col);
  270.                         tex.SetPixel(x, ( height / 2 ) - y, col);
  271.                     }
  272.                 }
  273.                 tex.Apply();
  274.             }
  275.             return tex;
  276.         }
  277.         */
  278.     }
  279.    
  280.     #endif
  281.  
  282.  
  283. }
RAW Paste Data