AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
AudioEventBankEditor.cs
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT License. See LICENSE in the project root for license information.
3 using System.Collections.Generic;
4 using UnityEngine;
5 using UnityEditor;
6 
7 namespace HoloToolkit.Unity
8 {
13  public class AudioBankEditor<TEvent> : Editor where TEvent : AudioEvent, new()
14  {
16  private string[] eventNames;
17  private int selectedEventIndex = 0;
18  private readonly string[] posTypes = { "2D", "3D", "Spatial Sound" };
19  private Rect editorCurveSize = new Rect(0f, 0f, 1f, 1f);
20 
21  protected void SetUpEditor()
22  {
23  // Having a null array of events causes too many errors and should only happen on first adding anyway.
24  if (this.MyTarget.Events == null)
25  {
26  this.MyTarget.Events = new TEvent[0];
27  }
28  this.eventNames = new string[this.MyTarget.Events.Length];
29  UpdateEventNames(this.MyTarget.Events);
30  }
31 
32  protected void DrawInspectorGUI(bool showEmitters)
33  {
34  this.serializedObject.Update();
35  EditorGUI.BeginChangeCheck();
36  DrawEventHeader(this.MyTarget.Events);
37 
38  if (this.MyTarget.Events != null && this.MyTarget.Events.Length > 0)
39  {
40  // Display current event in dropdown.
41  EditorGUI.indentLevel++;
42  this.selectedEventIndex = EditorGUILayout.Popup(this.selectedEventIndex, this.eventNames);
43 
44  if (this.selectedEventIndex < this.MyTarget.Events.Length)
45  {
46  TEvent selectedEvent;
47 
48  selectedEvent = this.MyTarget.Events[this.selectedEventIndex];
49  SerializedProperty selectedEventProperty = this.serializedObject.FindProperty("Events.Array.data[" + this.selectedEventIndex.ToString() + "]");
50  EditorGUILayout.Space();
51 
52  if (selectedEventProperty != null)
53  {
54  DrawEventInspector(selectedEventProperty, selectedEvent, this.MyTarget.Events, showEmitters);
55  if (!DrawContainerInspector(selectedEventProperty, selectedEvent))
56  {
57  EditorGUI.indentLevel++;
58  DrawSoundClipInspector(selectedEventProperty, selectedEvent);
59  EditorGUI.indentLevel--;
60  }
61  }
62 
63  EditorGUI.indentLevel--;
64  }
65  }
66 
67  EditorGUI.EndChangeCheck();
68  this.serializedObject.ApplyModifiedProperties();
69 
70  if (UnityEngine.GUI.changed)
71  {
72  EditorUtility.SetDirty(this.MyTarget);
73  }
74  }
75 
76  private void DrawEventHeader(TEvent[] EditorEvents)
77  {
78  // Add or remove current event.
79  EditorGUILayout.Space();
80  EditorGUILayout.BeginHorizontal();
82 
83  using (new EditorGUI.DisabledScope((EditorEvents != null) && (EditorEvents.Length < 1)))
84  {
85  if (EditorGUILayoutExtensions.Button("Remove"))
86  {
87  this.MyTarget.Events = RemoveAudioEvent(EditorEvents, this.selectedEventIndex);
88  }
89  }
90 
92  {
93  this.MyTarget.Events = AddAudioEvent(EditorEvents);
94  }
95 
96  EditorGUILayout.EndHorizontal();
97  EditorGUILayout.Space();
98  }
99 
100  private void DrawEventInspector(SerializedProperty selectedEventProperty, TEvent selectedEvent, TEvent[] EditorEvents, bool showEmitters)
101  {
102  // Get current event's properties.
103  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("Name"));
104 
105  if (selectedEvent.Name != this.eventNames[this.selectedEventIndex])
106  {
107  UpdateEventNames(EditorEvents);
108  }
109 
110  if (showEmitters)
111  {
112  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("PrimarySource"));
113  if (selectedEvent.IsContinuous())
114  {
115  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("SecondarySource"));
116  }
117  }
118 
119  // Positioning
120  selectedEvent.Spatialization = (SpatialPositioningType)EditorGUILayout.Popup("Positioning", (int)selectedEvent.Spatialization, this.posTypes);
121 
122  if (selectedEvent.Spatialization == SpatialPositioningType.SpatialSound)
123  {
124  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("RoomSize"));
125 
126  EditorGUILayout.Space();
127  }
128  if (selectedEvent.Spatialization == SpatialPositioningType.ThreeD || selectedEvent.Spatialization == SpatialPositioningType.SpatialSound)
129  {
130  float curveHeight = 30f;
131  float curveWidth = 300f;
132 
133  //Simple 3D Sounds properties
134  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("MaxDistanceAttenuation3D"));
135 
136  // You need to use the version of CurveField that takes a serialized property, otherwise the changes don't stick
137  SerializedProperty attenuationProperty = selectedEventProperty.FindPropertyRelative("AttenuationCurve");
138  SerializedProperty spatialProperty = selectedEventProperty.FindPropertyRelative("SpatialCurve");
139  SerializedProperty spreadProperty = selectedEventProperty.FindPropertyRelative("SpreadCurve");
140  //SerializedProperty lowPassProperty = selectedEventProperty.FindPropertyRelative("LowPassCurve");
141  SerializedProperty reverbProperty = selectedEventProperty.FindPropertyRelative("ReverbCurve");
142 
143  //volume attenuation red
144  EditorGUILayout.CurveField(attenuationProperty, Color.red, editorCurveSize, new GUIContent("Attenuation"), GUILayout.Height(curveHeight), GUILayout.Width(curveWidth), GUILayout.ExpandHeight(false), GUILayout.ExpandWidth(true));
145 
146  if (selectedEvent.Spatialization == SpatialPositioningType.ThreeD)
147  {
148  //spatial green
149  EditorGUILayout.CurveField(spatialProperty, Color.green, editorCurveSize, new GUIContent("Spatial"), GUILayout.Height(curveHeight), GUILayout.Width(curveWidth), GUILayout.ExpandHeight(false), GUILayout.ExpandWidth(true));
150  //spread lightblue
151  EditorGUILayout.CurveField(spreadProperty, Color.blue, editorCurveSize, new GUIContent("Spread"), GUILayout.Height(curveHeight), GUILayout.Width(curveWidth), GUILayout.ExpandHeight(false), GUILayout.ExpandWidth(true));
152  //lowpass purple
153  //EditorGUILayout.CurveField(lowPassProperty, Color.magenta, editorCurveSize, new GUIContent("LowPass"), GUILayout.Height(curveHeight), GUILayout.Width(curveWidth), GUILayout.ExpandHeight(false), GUILayout.ExpandWidth(true));
154  //reverb yellow
155  EditorGUILayout.CurveField(reverbProperty, Color.yellow, editorCurveSize, new GUIContent("Reverb"), GUILayout.Height(curveHeight), GUILayout.Width(curveWidth), GUILayout.ExpandHeight(false), GUILayout.ExpandWidth(true));
156  }
157 
158  EditorGUILayout.Space();
159  }
160 
161  // AudioBus
162  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("AudioBus"));
163 
164  // Fades
165  if (!selectedEvent.IsContinuous())
166  {
167  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("FadeInTime"));
168  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("FadeOutTime"));
169  }
170 
171  // Pitch Settings
172  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("PitchCenter"));
173 
174  // Volume settings
175  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("VolumeCenter"));
176 
177  // Pan Settings
178  if (selectedEvent.Spatialization == SpatialPositioningType.TwoD)
179  {
180  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("PanCenter"));
181  }
182 
183  // Instancing
184  EditorGUILayout.BeginHorizontal();
185  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("InstanceLimit"));
186  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("InstanceTimeBuffer"));
187  EditorGUILayout.EndHorizontal();
188  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("AudioEventInstanceBehavior"));
189 
190  // Container
191  EditorGUILayout.Space();
192  }
193 
194  private bool DrawContainerInspector(SerializedProperty selectedEventProperty, TEvent selectedEvent)
195  {
196  bool addedSound = false;
197  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("Container.ContainerType"));
198 
199  if (!selectedEvent.IsContinuous())
200  {
201  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("Container.Looping"));
202 
203  if (selectedEvent.Container.Looping)
204  {
205  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("Container.LoopTime"));
206  }
207  }
208 
209  // Sounds
210  EditorGUILayout.Space();
211 
212  if (selectedEvent.IsContinuous())
213  {
214  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("Container.CrossfadeTime"));
215  }
216 
217  EditorGUILayout.BeginHorizontal();
218  EditorGUILayout.LabelField("Sounds");
219 
221  {
222  AddSound(selectedEvent);
223 
224  // Skip drawing sound inspector after adding a new sound.
225  addedSound = true;
226  }
227  EditorGUILayout.EndHorizontal();
228  return addedSound;
229  }
230 
231  private void DrawSoundClipInspector(SerializedProperty selectedEventProperty, TEvent selectedEvent)
232  {
233  bool allowLoopingClip = !selectedEvent.Container.Looping;
234 
235  if (allowLoopingClip)
236  {
237  if (selectedEvent.IsContinuous())
238  {
239  allowLoopingClip = false;
240  }
241  }
242 
243  for (int i = 0; i < selectedEvent.Container.Sounds.Length; i++)
244  {
245  EditorGUILayout.Space();
246  EditorGUILayout.BeginHorizontal();
247  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("Container.Sounds.Array.data[" + i + "].Sound"));
248 
249  if (EditorGUILayoutExtensions.Button("Remove"))
250  {
251  selectedEventProperty.FindPropertyRelative("Container.Sounds.Array.data[" + i + "]").DeleteCommand();
252  break;
253  }
254 
255  EditorGUILayout.EndHorizontal();
256 
257  if (!selectedEvent.IsContinuous())
258  {
259  EditorGUILayout.BeginHorizontal();
260  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("Container.Sounds.Array.data[" + i + "].DelayCenter"));
261  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("Container.Sounds.Array.data[" + i + "].DelayRandomization"));
262  EditorGUILayout.EndHorizontal();
263 
264  //Disable looping next clips in a simultaneous container only.
265  if (allowLoopingClip)
266  {
267  EditorGUILayout.PropertyField(selectedEventProperty.FindPropertyRelative("Container.Sounds.Array.data[" + i + "].Looping"));
268 
269  if (selectedEvent.Container.Sounds[i].Looping && selectedEvent.Container.ContainerType == AudioContainerType.Simultaneous)
270  {
271  allowLoopingClip = false;
272  }
273  }
274  else
275  {
276  selectedEvent.Container.Sounds[i].Looping = false;
277  }
278  }
279  }
280  }
281 
282  private void UpdateEventNames(TEvent[] EditorEvents)
283  {
284  HashSet<string> previousEventNames = new HashSet<string>();
285 
286  for (int i = 0; i < EditorEvents.Length; i++)
287  {
288  if (string.IsNullOrEmpty(EditorEvents[i].Name))
289  {
290  EditorEvents[i].Name = "_NewEvent" + i.ToString();
291  }
292 
293  while (previousEventNames.Contains(EditorEvents[i].Name))
294  {
295  EditorEvents[i].Name = "_" + EditorEvents[i].Name;
296  }
297 
298  this.eventNames[i] = EditorEvents[i].Name;
299  previousEventNames.Add(this.eventNames[i]);
300  }
301  }
302 
303  private void AddSound(TEvent selectedEvent)
304  {
305 
306  UAudioClip[] tempClips = new UAudioClip[selectedEvent.Container.Sounds.Length + 1];
307  selectedEvent.Container.Sounds.CopyTo(tempClips, 0);
308  tempClips[tempClips.Length - 1] = new UAudioClip();
309  selectedEvent.Container.Sounds = tempClips;
310  }
311 
312  private TEvent[] AddAudioEvent(TEvent[] EditorEvents)
313  {
314  TEvent tempEvent = new TEvent();
315  TEvent[] tempEventArray = new TEvent[EditorEvents.Length + 1];
316  tempEvent.Container = new AudioContainer();
317  tempEvent.Container.Sounds = new UAudioClip[0];
318  EditorEvents.CopyTo(tempEventArray, 0);
319  tempEventArray[EditorEvents.Length] = tempEvent;
320  this.eventNames = new string[tempEventArray.Length];
321  UpdateEventNames(tempEventArray);
322  this.selectedEventIndex = this.eventNames.Length - 1;
323  return tempEventArray;
324  }
325 
326  private TEvent[] RemoveAudioEvent(TEvent[] editorEvents, int eventToRemove)
327  {
328  editorEvents = RemoveElement(editorEvents, eventToRemove);
329  this.eventNames = new string[editorEvents.Length];
330  UpdateEventNames(editorEvents);
331 
332  if (this.selectedEventIndex >= editorEvents.Length)
333  {
334  this.selectedEventIndex--;
335  }
336 
337  return editorEvents;
338  }
339 
346  public static T[] RemoveElement<T>(T[] array, int index)
347  {
348  T[] newArray = new T[array.Length - 1];
349 
350  for (int i = 0; i < array.Length; i++)
351  {
352  // Send the clip to the previous item in the new array if we have passed the removed clip.
353  if (i > index)
354  {
355  newArray[i - 1] = array[i];
356  }
357  else if (i < index)
358  {
359  newArray[i] = array[i];
360  }
361  }
362 
363  return newArray;
364  }
365  }
366 
367  [CustomEditor(typeof(AudioEventBank))]
368  public class AudioEventBankEditor : AudioBankEditor<AudioEvent>
369  {
370  private void OnEnable()
371  {
372  this.MyTarget = (AudioEventBank)target;
373  SetUpEditor();
374  }
375 
376  public override void OnInspectorGUI()
377  {
378  DrawInspectorGUI(false);
379  }
380  }
381 }
AudioContainerType
The different rules for how audio should be played back.
Definition: AudioEvent.cs:13
Instance of an AudioBank that contains AudioEvents
static bool Button(string text, params GUILayoutOption[] options)
SpatialPositioningType
The different types of spatial positioning.
Definition: AudioEvent.cs:34
Encapsulate a single Unity AudioClip with playback settings.
Definition: AudioClip.cs:12
Extensions for the UnityEditor.EditorGUILayout class.
The AudioEvent class is the main component of UAudioManager and contains settings and a container for...
Definition: AudioEvent.cs:54
Inspector for the AudioBank
The AudioContainer class is sound container for an AudioEvent. It also specifies the rules of how to ...
static void Label(string text, params GUILayoutOption[] options)
void DrawInspectorGUI(bool showEmitters)