PageRenderTime 30ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/MultiplayerMA/Assets/Plugins/DarkTonic/MasterAudio/Scripts/Singleton/MasterAudio.cs

https://gitlab.com/jerotas/MultiplayerMA
C# | 1266 lines | 995 code | 209 blank | 62 comment | 85 complexity | dc60249f4e6c5b1b6f235d25412b7cea MD5 | raw file
  1. using System.Linq;
  2. #if UNITY_EDITOR
  3. using UnityEditor;
  4. #endif
  5. using UnityEngine;
  6. using System;
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. using UnityEngine.Audio;
  10. using Debug = UnityEngine.Debug;
  11. using Random = UnityEngine.Random;
  12. #if UNITY_2019_3_OR_NEWER
  13. using UnityEngine.Video;
  14. #endif
  15. #if UNITY_XBOXONE
  16. using PlayerPrefs = DarkTonic.MasterAudio.FilePlayerPrefs;
  17. #endif
  18. // ReSharper disable once CheckNamespace
  19. namespace DarkTonic.MasterAudio {
  20. /// <summary>
  21. /// This class contains the heart of the Master Audio API. There are also convenience methods here for Playlist Controllers, even though you can call those methods on the Playlist Controller itself as well.
  22. /// </summary>
  23. // ReSharper disable once CheckNamespace
  24. [AudioScriptOrder(-50)]
  25. public class MasterAudio : MonoBehaviour {
  26. /*! \cond PRIVATE */
  27. #region Constants
  28. #pragma warning disable 1591
  29. public const string VideoPlayersSoundGroupSelectedError = "Can't use specially named Sound Group for Video Players. Please select another.";
  30. public const string VideoPlayerSoundGroupName = "_VideoPlayers";
  31. public const string VideoPlayerBusName = "_VideoPlayers";
  32. public const string MasterAudioDefaultFolder = "Assets/Plugins/DarkTonic/MasterAudio";
  33. public const string PreviewText = "Random delay, custom fading & start/end position settings are ignored by preview in edit mode.";
  34. public const string LoopDisabledLoopedChain = "Loop Clip is always OFF for Looped Chain Groups";
  35. public const string LoopDisabledCustomEnd = "Loop Clip is always OFF when using Custom End Position";
  36. public const string DragAudioTip = "Drag Audio clips or a folder containing some here";
  37. public const string NoCategory = "[Uncategorized]";
  38. public const float SemiTonePitchFactor = 1.05946f;
  39. public const float SpatialBlend_2DValue = 0f;
  40. public const float SpatialBlend_3DValue = 1f;
  41. public const float MaxCrossFadeTimeSeconds = 120;
  42. public const float DefaultDuckVolCut = -6f;
  43. // error numbers
  44. public const int ERROR_MA_LAYER_COLLISIONS_DISABLED = 1;
  45. public const int PHYSICS_DISABLED = 2;
  46. public const string StoredLanguageNameKey = "~MA_Language_Key~";
  47. public static readonly YieldInstruction EndOfFrameDelay = new WaitForEndOfFrame();
  48. public static readonly List<string> ExemptChildNames = new List<string> { AmbientUtil.FollowerHolderName };
  49. public static readonly HashSet<int> ErrorNumbersLogged = new HashSet<int>();
  50. public static List<string> ImportanceChoices = new List<string> { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" };
  51. #if ADDRESSABLES_ENABLED
  52. public static List<string> AddressableDeadIds = new List<string>();
  53. #endif
  54. /// <summary>
  55. /// Subscribe to this event to be notified when the number of Audio Sources being used by Master Audio changes.
  56. /// </summary>
  57. // ReSharper disable once RedundantNameQualifier
  58. public static System.Action NumberOfAudioSourcesChanged;
  59. public const int HardCodedBusOptions = 2;
  60. public const string AllBusesName = "[All]";
  61. public const string NoGroupName = "[None]";
  62. public const string DynamicGroupName = "[Type In]";
  63. public const string NoPlaylistName = "[No Playlist]";
  64. public const string NoVoiceLimitName = "[NO LMT]";
  65. public const string OnlyPlaylistControllerName = "~only~";
  66. public const float InnerLoopCheckInterval = .1f;
  67. private const int MaxComponents = 20;
  68. #endregion
  69. #region Public Variables
  70. // ReSharper disable InconsistentNaming
  71. public AudioLocation bulkLocationMode = AudioLocation.Clip;
  72. public string groupTemplateName = "Default Single";
  73. public string audioSourceTemplateName = "Max Distance 500";
  74. public bool showGroupCreation = true;
  75. public bool useGroupTemplates = false;
  76. public DragGroupMode curDragGroupMode = DragGroupMode.OneGroupPerClip;
  77. public List<GameObject> groupTemplates = new List<GameObject>(10);
  78. public List<GameObject> audioSourceTemplates = new List<GameObject>(10);
  79. public bool mixerMuted;
  80. public bool playlistsMuted;
  81. public LanguageMode langMode = LanguageMode.UseDeviceSetting;
  82. public SystemLanguage testLanguage = SystemLanguage.English;
  83. public SystemLanguage defaultLanguage = SystemLanguage.English;
  84. public List<SystemLanguage> supportedLanguages = new List<SystemLanguage>()
  85. {
  86. SystemLanguage.English
  87. };
  88. public string busFilter = string.Empty;
  89. public bool useTextGroupFilter = false;
  90. public string textGroupFilter = string.Empty;
  91. public bool resourceClipsPauseDoNotUnload = false;
  92. public Transform playlistControllerPrefab;
  93. public bool persistBetweenScenes = false;
  94. public bool shouldLogDestroys = false;
  95. public bool showBusColors = false;
  96. public bool showGroupImportance = false;
  97. public bool areGroupsExpanded = true;
  98. public Transform soundGroupTemplate;
  99. public Transform soundGroupVariationTemplate;
  100. public List<GroupBus> groupBuses = new List<GroupBus>();
  101. public bool groupByBus = true;
  102. public bool sortAlpha = true;
  103. public bool showRangeSoundGizmos = true;
  104. public bool showSelectedRangeSoundGizmos = true;
  105. public Color rangeGizmoColor = Color.green;
  106. public Color selectedRangeGizmoColor = Color.cyan;
  107. public bool showAdvancedSettings = true;
  108. public bool showLocalization = true;
  109. public bool showVideoPlayerSettings = false;
  110. #if UNITY_2019_3_OR_NEWER && VIDEO_ENABLED
  111. public List<VideoPlayer> videoPlayers = new List<VideoPlayer>();
  112. #endif
  113. public bool useTextPlaylistFilter = false;
  114. public string textPlaylistFilter = string.Empty;
  115. public bool playListExpanded = true;
  116. public bool playlistsExpanded = true;
  117. public AllMusicSpatialBlendType musicSpatialBlendType = AllMusicSpatialBlendType.ForceAllTo2D;
  118. public float musicSpatialBlend = 0f;
  119. #if DISABLE_3D_SOUND
  120. public AllMixerSpatialBlendType mixerSpatialBlendType = AllMixerSpatialBlendType.ForceAllTo2D;
  121. #else
  122. public AllMixerSpatialBlendType mixerSpatialBlendType = AllMixerSpatialBlendType.ForceAllTo3D;
  123. #endif
  124. public float mixerSpatialBlend = 1f;
  125. public GroupPlayType groupPlayType = GroupPlayType.Always;
  126. public DefaultGroupPlayType defaultGroupPlayType = DefaultGroupPlayType.Always;
  127. #if DISABLE_3D_SOUND
  128. public ItemSpatialBlendType newGroupSpatialType = ItemSpatialBlendType.ForceTo2D;
  129. #else
  130. public ItemSpatialBlendType newGroupSpatialType = ItemSpatialBlendType.ForceTo3D;
  131. #endif
  132. public float newGroupSpatialBlend = 1f;
  133. public List<Playlist> musicPlaylists = new List<Playlist>()
  134. {
  135. new Playlist()
  136. };
  137. public float _masterAudioVolume = 1.0f;
  138. public bool vrSettingsExpanded = false;
  139. public bool useSpatializer = false;
  140. public bool useSpatializerPostFX = false;
  141. public bool addOculusAudioSources = false;
  142. public bool addResonanceAudioSources = false;
  143. public bool ignoreTimeScale = false;
  144. public bool useGaplessPlaylists = false;
  145. public bool useGaplessAutoReschedule = false;
  146. public bool saveRuntimeChanges = false;
  147. public bool prioritizeOnDistance = false;
  148. public int rePrioritizeEverySecIndex = 1;
  149. public bool useOcclusion = false;
  150. public float occlusionMaxCutoffFreq = AudioUtil.DefaultMaxOcclusionCutoffFrequency;
  151. public float occlusionMinCutoffFreq = AudioUtil.DefaultMinOcclusionCutoffFrequency;
  152. public float occlusionFreqChangeSeconds = 0f;
  153. public OcclusionSelectionType occlusionSelectType = OcclusionSelectionType.AllGroups;
  154. public int occlusionMaxRayCastsPerFrame = 4;
  155. public float occlusionRayCastOffset = 0f;
  156. public bool occlusionUseLayerMask;
  157. public LayerMask occlusionLayerMask;
  158. public bool occlusionShowRaycasts = true;
  159. public bool occlusionShowCategories = false;
  160. public RaycastMode occlusionRaycastMode = RaycastMode.Physics3D;
  161. public bool occlusionIncludeStartRaycast2DCollider = true;
  162. public bool occlusionRaycastsHitTriggers = true;
  163. public bool ambientAdvancedExpanded = false;
  164. public int ambientMaxRecalcsPerFrame = 4;
  165. public bool visualAdvancedExpanded = true;
  166. public bool logAdvancedExpanded = true;
  167. public bool listenerAdvancedExpanded = false;
  168. public bool listenerFollowerHasRigidBody = true;
  169. public bool deletePreviewerAudioSourceWhenPlaying = true;
  170. public VariationFollowerType variationFollowerType = VariationFollowerType.LateUpdate;
  171. public bool showFadingSettings = false;
  172. public bool stopZeroVolumeGroups = false;
  173. public bool stopZeroVolumeBuses = false;
  174. public bool stopZeroVolumePlaylists = false;
  175. public float stopOldestBusFadeTime = 0.3f;
  176. public bool resourceAdvancedExpanded = true;
  177. public bool useClipAgePriority = false;
  178. public bool logOutOfVoices = true;
  179. public bool LogSounds;
  180. public bool logCustomEvents = false;
  181. public bool disableLogging = false;
  182. public bool showMusicDucking = false;
  183. public bool enableMusicDucking = true;
  184. public List<DuckGroupInfo> musicDuckingSounds = new List<DuckGroupInfo>();
  185. public float defaultRiseVolStart = .5f;
  186. public float defaultUnduckTime = 1f;
  187. public float defaultDuckedVolumeCut = DefaultDuckVolCut;
  188. public float crossFadeTime = 1f;
  189. public float _masterPlaylistVolume = 1f;
  190. public bool showGroupSelect = false;
  191. public bool hideGroupsWithNoActiveVars = false;
  192. public JukeBoxDisplayMode jukeBoxDisplayMode = JukeBoxDisplayMode.DisplayAll;
  193. public bool logPerfExpanded = true;
  194. public bool willWarm = true;
  195. public bool mixerSettingsExpanded;
  196. public AudioMixerUpdateMode mixerUpdateMode = AudioMixerUpdateMode.UnscaledTime;
  197. public string newEventName = "my event";
  198. public bool showCustomEvents = true;
  199. public string newCustomEventCategoryName = "New Category";
  200. public string addToCustomEventCategoryName = "New Category";
  201. public List<CustomEvent> customEvents = new List<CustomEvent>();
  202. public List<CustomEventCategory> customEventCategories = new List<CustomEventCategory> {
  203. new CustomEventCategory()
  204. };
  205. public Dictionary<string, DuckGroupInfo> duckingBySoundType = new Dictionary<string, DuckGroupInfo>(StringComparer.OrdinalIgnoreCase);
  206. // populated at runtime
  207. #if ADDRESSABLES_ENABLED
  208. //public SortedCache<int> addressablesToReleaseAfterSecondsByAddressableId
  209. #endif
  210. public int frames;
  211. public bool showUnityMixerGroupAssignment = true;
  212. public static readonly PlaySoundResult AndForgetSuccessResult = new PlaySoundResult {
  213. SoundPlayed = true
  214. };
  215. private static readonly PlaySoundResult failedResultDuringInit = new PlaySoundResult {
  216. SoundPlayed = false
  217. };
  218. #endregion
  219. #region Private Variables
  220. private readonly Dictionary<string, AudioGroupInfo> AudioSourcesBySoundType =
  221. new Dictionary<string, AudioGroupInfo>(StringComparer.OrdinalIgnoreCase);
  222. private Dictionary<string, List<int>> _randomizer = new Dictionary<string, List<int>>(StringComparer.OrdinalIgnoreCase);
  223. private Dictionary<string, List<int>> _randomizerOrigin = new Dictionary<string, List<int>>(StringComparer.OrdinalIgnoreCase);
  224. private Dictionary<string, List<int>> _randomizerLeftovers = new Dictionary<string, List<int>>(StringComparer.OrdinalIgnoreCase);
  225. private Dictionary<string, List<int>> _nonRandomChoices = new Dictionary<string, List<int>>(StringComparer.OrdinalIgnoreCase);
  226. private Dictionary<string, List<int>> _clipsPlayedBySoundTypeOldestFirst = new Dictionary<string, List<int>>(StringComparer.OrdinalIgnoreCase);
  227. private readonly List<SoundGroupVariationUpdater> ActiveVariationUpdaters = new List<SoundGroupVariationUpdater>(32);
  228. private readonly List<SoundGroupVariationUpdater> ActiveUpdatersToRemove = new List<SoundGroupVariationUpdater>();
  229. private readonly List<ICustomEventReceiver> ValidReceivers = new List<ICustomEventReceiver>();
  230. private readonly List<CustomEventCandidate> ValidReceiverCandidates = new List<CustomEventCandidate>(10);
  231. private readonly List<MasterAudioGroup> SoloedGroups = new List<MasterAudioGroup>();
  232. private readonly List<AmbientSoundToTriggerInfo> AmbientsToDelayedTrigger = new List<AmbientSoundToTriggerInfo>();
  233. private readonly Queue<CustomEventToFireInfo> CustomEventsToFire = new Queue<CustomEventToFireInfo>(32);
  234. private readonly Queue<TransformFollower> TransFollowerColliderPositionRecalcs = new Queue<TransformFollower>(32);
  235. private readonly List<TransformFollower> ProcessedColliderPositionRecalcs = new List<TransformFollower>(32);
  236. private readonly List<BusFadeInfo> BusFades = new List<BusFadeInfo>(2);
  237. private readonly List<GroupFadeInfo> GroupFades = new List<GroupFadeInfo>();
  238. private readonly List<GroupPitchGlideInfo> GroupPitchGlides = new List<GroupPitchGlideInfo>();
  239. private readonly List<BusPitchGlideInfo> BusPitchGlides = new List<BusPitchGlideInfo>();
  240. private readonly List<OcclusionFreqChangeInfo> VariationOcclusionFreqChanges = new List<OcclusionFreqChangeInfo>();
  241. private readonly List<AudioSource> AllAudioSources = new List<AudioSource>();
  242. private readonly Dictionary<string, Dictionary<ICustomEventReceiver, Transform>> ReceiversByEventName =
  243. new Dictionary<string, Dictionary<ICustomEventReceiver, Transform>>(StringComparer.OrdinalIgnoreCase);
  244. private readonly Dictionary<string, PlaylistController> PlaylistControllersByName =
  245. new Dictionary<string, PlaylistController>(StringComparer.OrdinalIgnoreCase);
  246. private readonly Dictionary<string, SoundGroupRefillInfo> LastTimeSoundGroupPlayed =
  247. new Dictionary<string, SoundGroupRefillInfo>(StringComparer.OrdinalIgnoreCase);
  248. private readonly List<GameObject> OcclusionSourcesInRange = new List<GameObject>(32);
  249. private readonly List<GameObject> OcclusionSourcesOutOfRange = new List<GameObject>(32);
  250. private readonly List<GameObject> OcclusionSourcesBlocked = new List<GameObject>(32);
  251. private readonly Queue<SoundGroupVariationUpdater> QueuedOcclusionRays = new Queue<SoundGroupVariationUpdater>(32);
  252. #if UNITY_2019_3_OR_NEWER && VIDEO_ENABLED
  253. private readonly List<VideoPlayerTracker> VideoPlayerTrackers = new List<VideoPlayerTracker>();
  254. #endif
  255. #if ADDRESSABLES_ENABLED
  256. private readonly List<AddressableDelayedRelease> AddressablesToReleaseLater = new List<AddressableDelayedRelease>();
  257. #endif
  258. private readonly List<string> AllSoundGroupNames = new List<string>(32); // use this to loop through the list. Looping on .Keys of a Dictionary allocates.
  259. private readonly List<string> AllBusNames = new List<string>(32); // use this to loop through the list. Looping on .Keys of a Dictionary allocates.
  260. private readonly List<AudioInfo> GroupsToDelete = new List<AudioInfo>();
  261. private readonly List<SoundGroupVariation> VariationsStartedDuringMultiStop = new List<SoundGroupVariation>(16);
  262. private readonly List<PlaylistController> ControllersToPause = new List<PlaylistController>();
  263. private readonly List<PlaylistController> ControllersToUnpause = new List<PlaylistController>();
  264. private readonly List<PlaylistController> ControllersToMute = new List<PlaylistController>();
  265. private readonly List<PlaylistController> ControllersToUnmute = new List<PlaylistController>();
  266. private readonly List<PlaylistController> ControllersToToggleMute = new List<PlaylistController>();
  267. private readonly List<PlaylistController> ControllersToStop = new List<PlaylistController>();
  268. private readonly List<PlaylistController> ControllersToFade = new List<PlaylistController>();
  269. private readonly List<PlaylistController> ControllersToTrigNext = new List<PlaylistController>();
  270. private readonly List<PlaylistController> ControllersToTrigRandom = new List<PlaylistController>();
  271. private readonly List<PlaylistController> ControllersToStart = new List<PlaylistController>();
  272. private readonly List<AmbientSoundToTriggerInfo> AmbientsToTriggerNow = new List<AmbientSoundToTriggerInfo>();
  273. private bool _isStoppingMultiple;
  274. private float _repriTime = -1f;
  275. private List<string> _groupsToRemove;
  276. private bool _mustRescanGroups;
  277. private Transform _trans;
  278. private bool _soundsLoaded;
  279. private bool _warming;
  280. // ReSharper restore InconsistentNaming
  281. private static MasterAudio _instance;
  282. private static string _prospectiveMAFolder = string.Empty;
  283. private static Transform _listenerTrans;
  284. #endregion
  285. #region Master Audio enums
  286. public enum JukeBoxDisplayMode
  287. {
  288. DisplayAll,
  289. DisplayActive
  290. }
  291. public enum BusVoiceLimitExceededMode
  292. {
  293. DoNotPlayNewSound,
  294. StopOldestSound,
  295. StopFarthestSound,
  296. StopLeastImportantSound
  297. }
  298. public enum AmbientSoundExitMode {
  299. StopSound,
  300. FadeSound
  301. }
  302. public enum AmbientSoundReEnterMode {
  303. StopExistingSound,
  304. FadeInSameSound
  305. }
  306. public enum VariationFollowerType {
  307. LateUpdate,
  308. FixedUpdate
  309. }
  310. public enum LinkedGroupSelectionType {
  311. All,
  312. OneAtRandom
  313. }
  314. public enum OcclusionSelectionType {
  315. AllGroups,
  316. TurnOnPerBusOrGroup
  317. }
  318. public enum RaycastMode {
  319. Physics3D,
  320. Physics2D
  321. }
  322. public enum AllMusicSpatialBlendType {
  323. ForceAllTo2D
  324. #if DISABLE_3D_SOUND
  325. #else
  326. ,
  327. ForceAllTo3D,
  328. ForceAllToCustom,
  329. AllowDifferentPerController
  330. #endif
  331. }
  332. public enum AllMixerSpatialBlendType {
  333. ForceAllTo2D
  334. #if DISABLE_3D_SOUND
  335. #else
  336. ,
  337. ForceAllTo3D,
  338. ForceAllToCustom,
  339. AllowDifferentPerGroup
  340. #endif
  341. }
  342. public enum ItemSpatialBlendType {
  343. ForceTo2D
  344. #if DISABLE_3D_SOUND
  345. #else
  346. ,
  347. ForceTo3D,
  348. ForceToCustom,
  349. UseCurveFromAudioSource
  350. #endif
  351. }
  352. public enum GroupPlayType
  353. {
  354. Always,
  355. WhenActorInAudibleRange,
  356. AllowDifferentPerGroup
  357. }
  358. public enum DefaultGroupPlayType {
  359. Always,
  360. WhenActorInAudibleRange
  361. }
  362. public enum MixerWidthMode {
  363. Narrow,
  364. Normal,
  365. Wide
  366. }
  367. public enum CustomEventReceiveMode {
  368. Always,
  369. WhenDistanceLessThan,
  370. WhenDistanceMoreThan,
  371. Never,
  372. OnSameGameObject,
  373. OnChildGameObject,
  374. OnParentGameObject,
  375. OnSameOrChildGameObject,
  376. OnSameOrParentGameObject
  377. }
  378. public enum EventReceiveFilter {
  379. All,
  380. Closest,
  381. Random
  382. }
  383. public enum VariationLoadStatus {
  384. None,
  385. Loading,
  386. Loaded,
  387. LoadFailed
  388. }
  389. /*! \endcond */
  390. /// <summary>
  391. /// This setting lets you choose where the Audio Clip lives: Clip, Resource File or Addressable
  392. /// </summary>
  393. public enum AudioLocation {
  394. Clip,
  395. ResourceFile
  396. #if ADDRESSABLES_ENABLED
  397. ,Addressable
  398. #endif
  399. }
  400. /// <summary>
  401. /// This controls where the song starts, Beginning, Specific Time or Random Time.
  402. /// </summary>
  403. public enum CustomSongStartTimeMode
  404. {
  405. Beginning,
  406. SpecificTime,
  407. RandomTime,
  408. Section
  409. }
  410. /*! \cond PRIVATE */
  411. public enum BusCommand {
  412. None,
  413. FadeToVolume,
  414. Mute,
  415. Pause,
  416. Solo,
  417. Unmute,
  418. Unpause,
  419. Unsolo,
  420. Stop,
  421. ChangePitch,
  422. ToggleMute,
  423. StopBusOfTransform,
  424. PauseBusOfTransform,
  425. UnpauseBusOfTransform,
  426. GlideByPitch,
  427. StopOldBusVoices,
  428. FadeOutOldBusVoices
  429. }
  430. public enum DragGroupMode {
  431. OneGroupPerClip,
  432. OneGroupWithVariations
  433. }
  434. public enum EventSoundFunctionType {
  435. PlaySound,
  436. GroupControl,
  437. BusControl,
  438. PlaylistControl,
  439. CustomEventControl,
  440. GlobalControl,
  441. UnityMixerControl,
  442. PersistentSettingsControl
  443. }
  444. public enum LanguageMode {
  445. UseDeviceSetting,
  446. SpecificLanguage,
  447. DynamicallySet
  448. }
  449. public enum UnityMixerCommand {
  450. None,
  451. TransitionToSnapshot,
  452. TransitionToSnapshotBlend
  453. }
  454. public enum PlaylistCommand {
  455. None,
  456. ChangePlaylist, // by name
  457. FadeToVolume,
  458. PlaySong, // by name
  459. PlayRandomSong,
  460. PlayNextSong,
  461. Pause,
  462. Resume,
  463. Stop,
  464. Mute,
  465. Unmute,
  466. ToggleMute,
  467. Restart,
  468. Start,
  469. StopLoopingCurrentSong,
  470. StopPlaylistAfterCurrentSong,
  471. AddSongToQueue
  472. }
  473. public enum CustomEventCommand {
  474. None,
  475. FireEvent
  476. }
  477. public enum GlobalCommand {
  478. None,
  479. PauseMixer,
  480. UnpauseMixer,
  481. StopMixer,
  482. StopEverything,
  483. PauseEverything,
  484. UnpauseEverything,
  485. MuteEverything,
  486. UnmuteEverything,
  487. SetMasterMixerVolume,
  488. SetMasterPlaylistVolume,
  489. PauseAudioListener,
  490. UnpauseAudioListener
  491. }
  492. public enum SoundGroupCommand {
  493. None,
  494. FadeToVolume,
  495. FadeOutAllOfSound,
  496. Mute,
  497. Pause,
  498. Solo,
  499. StopAllOfSound,
  500. Unmute,
  501. Unpause,
  502. Unsolo,
  503. StopAllSoundsOfTransform,
  504. PauseAllSoundsOfTransform,
  505. UnpauseAllSoundsOfTransform,
  506. StopSoundGroupOfTransform,
  507. PauseSoundGroupOfTransform,
  508. UnpauseSoundGroupOfTransform,
  509. FadeOutSoundGroupOfTransform,
  510. RefillSoundGroupPool,
  511. RouteToBus,
  512. GlideByPitch,
  513. ToggleSoundGroup,
  514. ToggleSoundGroupOfTransform,
  515. FadeOutAllSoundsOfTransform,
  516. StopOldSoundGroupVoices,
  517. FadeOutOldSoundGroupVoices
  518. }
  519. public enum PersistentSettingsCommand {
  520. None,
  521. SetBusVolume,
  522. SetGroupVolume,
  523. SetMixerVolume,
  524. SetMusicVolume,
  525. MixerMuteToggle,
  526. MusicMuteToggle
  527. }
  528. public enum SongFadeInPosition {
  529. NewClipFromBeginning = 1,
  530. NewClipFromLastKnownPosition = 3,
  531. SynchronizeClips = 5,
  532. }
  533. public enum SoundSpawnLocationMode {
  534. MasterAudioLocation,
  535. CallerLocation,
  536. AttachToCaller
  537. }
  538. public enum VariationCommand {
  539. None = 0,
  540. Stop = 1,
  541. Pause = 2,
  542. Unpause = 3
  543. }
  544. public static readonly List<SoundGroupCommand> GroupCommandsWithNoGroupSelector = new List<SoundGroupCommand> {
  545. SoundGroupCommand.None,
  546. SoundGroupCommand.PauseAllSoundsOfTransform,
  547. SoundGroupCommand.StopAllSoundsOfTransform,
  548. SoundGroupCommand.UnpauseAllSoundsOfTransform,
  549. SoundGroupCommand.FadeOutAllSoundsOfTransform
  550. };
  551. public static readonly List<SoundGroupCommand> GroupCommandsWithNoAllGroupSelector = new List<SoundGroupCommand> {
  552. SoundGroupCommand.None,
  553. SoundGroupCommand.FadeOutSoundGroupOfTransform,
  554. SoundGroupCommand.PauseSoundGroupOfTransform,
  555. SoundGroupCommand.UnpauseSoundGroupOfTransform,
  556. SoundGroupCommand.StopSoundGroupOfTransform,
  557. SoundGroupCommand.ToggleSoundGroupOfTransform,
  558. SoundGroupCommand.ToggleSoundGroup,
  559. SoundGroupCommand.FadeOutAllSoundsOfTransform
  560. };
  561. #endregion
  562. #region Inner classes & Structs
  563. [Serializable]
  564. public struct CustomEventCandidate {
  565. public float DistanceAway;
  566. public ICustomEventReceiver Receiver;
  567. public Transform Trans;
  568. public int RandomId;
  569. public CustomEventCandidate(float distance, ICustomEventReceiver rec, Transform trans, int randomId) {
  570. DistanceAway = distance;
  571. Receiver = rec;
  572. Trans = trans;
  573. RandomId = randomId;
  574. }
  575. }
  576. [Serializable]
  577. public class AudioGroupInfo {
  578. public List<AudioInfo> Sources;
  579. public int LastFramePlayed;
  580. public float LastTimePlayed;
  581. public MasterAudioGroup Group;
  582. public bool PlayedForWarming;
  583. public AudioGroupInfo(List<AudioInfo> sources, MasterAudioGroup groupScript) {
  584. Sources = sources;
  585. LastFramePlayed = -50;
  586. LastTimePlayed = -50;
  587. Group = groupScript;
  588. PlayedForWarming = false;
  589. }
  590. }
  591. [Serializable]
  592. public class AudioInfo {
  593. public AudioSource Source;
  594. public float OriginalVolume;
  595. public float LastPercentageVolume;
  596. public float LastRandomVolume;
  597. public SoundGroupVariation Variation;
  598. public AudioInfo(SoundGroupVariation variation, AudioSource source, float origVol) {
  599. Variation = variation;
  600. Source = source;
  601. OriginalVolume = origVol;
  602. LastPercentageVolume = 1f;
  603. LastRandomVolume = 0f;
  604. }
  605. }
  606. [Serializable]
  607. public class Playlist {
  608. // ReSharper disable InconsistentNaming
  609. public bool isExpanded = true;
  610. public string playlistName = "new playlist";
  611. public SongFadeInPosition songTransitionType = SongFadeInPosition.NewClipFromBeginning;
  612. public List<MusicSetting> MusicSettings;
  613. public AudioLocation bulkLocationMode = AudioLocation.Clip;
  614. public CrossfadeTimeMode crossfadeMode = CrossfadeTimeMode.UseMasterSetting;
  615. public float crossFadeTime = 1f;
  616. public bool fadeInFirstSong = false;
  617. public bool fadeOutLastSong = false;
  618. public bool bulkEditMode = false;
  619. public bool isTemporary = false;
  620. public bool showMetadata = false;
  621. public List<SongMetadataProperty> songMetadataProps = new List<SongMetadataProperty>();
  622. public string newMetadataPropName = "PropertyName";
  623. public SongMetadataProperty.MetadataPropertyType newMetadataPropType = SongMetadataProperty.MetadataPropertyType.String;
  624. public bool newMetadataPropRequired = true;
  625. public bool newMetadataPropCanHaveMult = false;
  626. // ReSharper restore InconsistentNaming
  627. private readonly List<int> _actorInstanceIds = new List<int>();
  628. public enum CrossfadeTimeMode {
  629. UseMasterSetting,
  630. Override
  631. }
  632. public Playlist() {
  633. MusicSettings = new List<MusicSetting>();
  634. }
  635. public void AddActorInstanceId(int instanceId)
  636. {
  637. if (_actorInstanceIds.Contains(instanceId))
  638. {
  639. return;
  640. }
  641. _actorInstanceIds.Add(instanceId);
  642. }
  643. public void RemoveActorInstanceId(int instanceId)
  644. {
  645. _actorInstanceIds.Remove(instanceId);
  646. }
  647. public bool HasLiveActors {
  648. get {
  649. return _actorInstanceIds.Count > 0;
  650. }
  651. }
  652. }
  653. [Serializable]
  654. public class SoundGroupRefillInfo {
  655. public float LastTimePlayed;
  656. public float InactivePeriodSeconds;
  657. public SoundGroupRefillInfo(float lastTimePlayed, float inactivePeriodSeconds) {
  658. LastTimePlayed = lastTimePlayed;
  659. InactivePeriodSeconds = inactivePeriodSeconds;
  660. }
  661. }
  662. /*! \endcond */
  663. #endregion
  664. #region MonoDevelop events and Helpers
  665. #if UNITY_2019_3_OR_NEWER
  666. [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
  667. static void Init() {
  668. AppIsShuttingDown = false;
  669. }
  670. #endif
  671. // ReSharper disable once UnusedMember.Local
  672. // ReSharper disable once FunctionComplexityOverflow
  673. private void Awake() {
  674. var mas = FindObjectsOfType(typeof(MasterAudio));
  675. if (mas.Length > 1) {
  676. Destroy(gameObject);
  677. bool shouldLog = false;
  678. for (var i = 0; i < mas.Length; i++) {
  679. MasterAudio ama = mas[i] as MasterAudio;
  680. if (!ama.persistBetweenScenes) {
  681. continue;
  682. }
  683. if (ama.shouldLogDestroys) {
  684. shouldLog = true;
  685. break;
  686. }
  687. }
  688. if (shouldLog) {
  689. Debug.Log("More than one Master Audio prefab exists in this Scene. Destroying the newer one called '" +
  690. name + "'. You may wish to set up a Bootstrapper Scene so this does not occur.");
  691. }
  692. return;
  693. }
  694. AudioListener.pause = false; // in case you exited last time with it paused. You would hear nothing on next play.
  695. useGUILayout = false;
  696. _soundsLoaded = false;
  697. _mustRescanGroups = false;
  698. var listener = ListenerTrans;
  699. if (listener != null && deletePreviewerAudioSourceWhenPlaying) {
  700. var aud = listener.GetComponent<AudioSource>();
  701. if (aud != null) {
  702. // delete the previewer
  703. // ReSharper disable once ArrangeStaticMemberQualifier
  704. GameObject.Destroy(aud);
  705. }
  706. }
  707. AmbientUtil.InitFollowerHolder();
  708. AudioSourcesBySoundType.Clear();
  709. AllBusNames.Clear();
  710. AllSoundGroupNames.Clear();
  711. GroupsToDelete.Clear();
  712. ValidReceivers.Clear();
  713. ValidReceiverCandidates.Clear();
  714. ControllersToPause.Clear();
  715. ControllersToUnpause.Clear();
  716. ControllersToMute.Clear();
  717. ControllersToUnmute.Clear();
  718. ControllersToToggleMute.Clear();
  719. ControllersToStop.Clear();
  720. ControllersToFade.Clear();
  721. ControllersToTrigNext.Clear();
  722. ControllersToTrigRandom.Clear();
  723. ControllersToStart.Clear();
  724. PlaylistControllersByName.Clear();
  725. LastTimeSoundGroupPlayed.Clear();
  726. ErrorNumbersLogged.Clear();
  727. AmbientsToTriggerNow.Clear();
  728. AllAudioSources.Clear();
  729. OcclusionSourcesInRange.Clear();
  730. OcclusionSourcesOutOfRange.Clear();
  731. OcclusionSourcesBlocked.Clear();
  732. QueuedOcclusionRays.Clear();
  733. TransFollowerColliderPositionRecalcs.Clear();
  734. CustomEventsToFire.Clear();
  735. AmbientsToDelayedTrigger.Clear();
  736. ProcessedColliderPositionRecalcs.Clear();
  737. ActiveVariationUpdaters.Clear();
  738. ActiveUpdatersToRemove.Clear();
  739. var plNames = new List<string>();
  740. AudioResourceOptimizer.ClearAudioClips();
  741. PlaylistController.Instances = null; // clear the cache
  742. var playlists = PlaylistController.Instances;
  743. // ReSharper disable once ForCanBeConvertedToForeach
  744. for (var i = 0; i < playlists.Count; i++) {
  745. var aList = playlists[i];
  746. if (plNames.Contains(aList.ControllerName)) {
  747. Debug.LogError("You have more than 1 Playlist Controller with the name '" + aList.ControllerName +
  748. "'. You must name them all uniquely or the same-named ones will be deleted once they awake.");
  749. continue;
  750. }
  751. plNames.Add(aList.ControllerName);
  752. PlaylistControllersByName.Add(aList.ControllerName, aList);
  753. if (persistBetweenScenes) {
  754. DontDestroyOnLoad(aList);
  755. }
  756. }
  757. // start up Objects!
  758. if (persistBetweenScenes) {
  759. DontDestroyOnLoad(gameObject);
  760. }
  761. var playedStatuses = new List<int>();
  762. // ReSharper disable TooWideLocalVariableScope
  763. Transform parentGroup;
  764. List<AudioInfo> sources;
  765. AudioSource source;
  766. AudioGroupInfo group;
  767. MasterAudioGroup groupScript;
  768. string soundType;
  769. // ReSharper restore TooWideLocalVariableScope
  770. _randomizer = new Dictionary<string, List<int>>(StringComparer.OrdinalIgnoreCase);
  771. _randomizerOrigin = new Dictionary<string, List<int>>(StringComparer.OrdinalIgnoreCase);
  772. _randomizerLeftovers = new Dictionary<string, List<int>>(StringComparer.OrdinalIgnoreCase);
  773. _nonRandomChoices = new Dictionary<string, List<int>>(StringComparer.OrdinalIgnoreCase);
  774. _clipsPlayedBySoundTypeOldestFirst = new Dictionary<string, List<int>>(StringComparer.OrdinalIgnoreCase);
  775. var allVars = new List<SoundGroupVariation>();
  776. _groupsToRemove = new List<string>(Trans.childCount);
  777. var mutedGroups = new List<string>();
  778. for (var k = 0; k < Trans.childCount; k++) {
  779. parentGroup = Trans.GetChild(k);
  780. sources = new List<AudioInfo>();
  781. groupScript = parentGroup.GetComponent<MasterAudioGroup>();
  782. if (groupScript == null) {
  783. if (!ArrayListUtil.IsExcludedChildName(parentGroup.name)) {
  784. Debug.LogError("MasterAudio could not find 'MasterAudioGroup' script for group '" + parentGroup.name + "'. Skipping this group.");
  785. }
  786. continue;
  787. }
  788. soundType = groupScript.GameObjectName;
  789. var newWeightedChildren = new List<Transform>();
  790. // ReSharper disable TooWideLocalVariableScope
  791. SoundGroupVariation variation;
  792. SoundGroupVariation childVariation;
  793. Transform child;
  794. // ReSharper restore TooWideLocalVariableScope
  795. var allStatuses = new List<int>();
  796. for (var i = 0; i < parentGroup.childCount; i++) {
  797. child = parentGroup.GetChild(i);
  798. variation = child.GetComponent<SoundGroupVariation>();
  799. source = child.GetComponent<AudioSource>();
  800. var weight = variation.weight;
  801. for (var j = 0; j < weight; j++) {
  802. if (j > 0) {
  803. // ReSharper disable once ArrangeStaticMemberQualifier
  804. var extraChild = (GameObject)GameObject.Instantiate(child.gameObject, parentGroup.transform.position, Quaternion.identity);
  805. extraChild.transform.name = child.gameObject.name;
  806. childVariation = extraChild.GetComponent<SoundGroupVariation>();
  807. childVariation.weight = 1;
  808. newWeightedChildren.Add(extraChild.transform);
  809. source = extraChild.GetComponent<AudioSource>();
  810. sources.Add(new AudioInfo(childVariation, source, source.volume));
  811. allVars.Add(childVariation);
  812. switch (childVariation.audLocation) {
  813. case AudioLocation.ResourceFile:
  814. AudioResourceOptimizer.AddTargetForClip(childVariation.resourceFileName, source);
  815. break;
  816. }
  817. } else {
  818. sources.Add(new AudioInfo(variation, source, source.volume));
  819. allVars.Add(variation);
  820. switch (variation.audLocation) {
  821. case AudioLocation.ResourceFile:
  822. var resFileName =
  823. AudioResourceOptimizer.GetLocalizedFileName(variation.useLocalization,
  824. variation.resourceFileName);
  825. AudioResourceOptimizer.AddTargetForClip(resFileName, source);
  826. break;
  827. }
  828. }
  829. }
  830. }
  831. // attach extra children from weight property.
  832. // ReSharper disable once ForCanBeConvertedToForeach
  833. for (var i = 0; i < newWeightedChildren.Count; i++) {
  834. newWeightedChildren[i].parent = parentGroup;
  835. }
  836. group = new AudioGroupInfo(sources, groupScript);
  837. if (groupScript.isSoloed) {
  838. SoloedGroups.Add(groupScript);
  839. }
  840. if (groupScript.isMuted) {
  841. if (mutedGroups.Contains(groupScript.GameObjectName)) {
  842. continue;
  843. }
  844. mutedGroups.Add(groupScript.GameObjectName);
  845. }
  846. if (AudioSourcesBySoundType.ContainsKey(soundType)) {
  847. Debug.LogError("You have more than one SoundGroup named '" + soundType +
  848. "'. Ignoring the 2nd one. Please rename it.");
  849. continue;
  850. }
  851. group.Group.OriginalVolume = group.Group.groupMasterVolume;
  852. // added code for persistent group volume
  853. var persistentVolume = PersistentAudioSettings.GetGroupVolume(soundType);
  854. if (persistentVolume.HasValue) {
  855. group.Group.groupMasterVolume = persistentVolume.Value;
  856. }
  857. AddRuntimeGroupInfo(soundType, group);
  858. for (var i = 0; i < sources.Count; i++) {
  859. playedStatuses.Add(i);
  860. }
  861. if (group.Group.curVariationSequence == MasterAudioGroup.VariationSequence.Randomized) {
  862. ArrayListUtil.SortIntArray(ref playedStatuses);
  863. }
  864. _randomizer.Add(soundType, playedStatuses);
  865. allStatuses.Clear();
  866. allStatuses.AddRange(playedStatuses);
  867. _randomizerOrigin.Add(soundType, allStatuses); // must be a copy of one gets lost later
  868. _randomizerLeftovers.Add(soundType, new List<int>(playedStatuses.Count));
  869. // fill leftovers pool.
  870. _randomizerLeftovers[soundType].AddRange(playedStatuses);
  871. _clipsPlayedBySoundTypeOldestFirst.Add(soundType, new List<int>());
  872. _nonRandomChoices.Add(soundType, new List<int>());
  873. playedStatuses = new List<int>();
  874. }
  875. GroupFades.Clear();
  876. BusFades.Clear();
  877. GroupPitchGlides.Clear();
  878. BusPitchGlides.Clear();
  879. VariationOcclusionFreqChanges.Clear();
  880. // initialize persistent bus volumes
  881. // ReSharper disable once ForCanBeConvertedToForeach
  882. for (var i = 0; i < groupBuses.Count; i++) {
  883. var aBus = groupBuses[i];
  884. aBus.OriginalVolume = aBus.volume;
  885. var busName = aBus.busName;
  886. var busVol = PersistentAudioSettings.GetBusVolume(busName);
  887. if (!busVol.HasValue) {
  888. continue;
  889. }
  890. SetBusVolumeByName(busName, busVol.Value);
  891. }
  892. // populate ducking sounds dictionary
  893. duckingBySoundType.Clear();
  894. // ReSharper disable once ForCanBeConvertedToForeach
  895. for (var i = 0; i < musicDuckingSounds.Count; i++) {
  896. var aDuck = musicDuckingSounds[i];
  897. if (duckingBySoundType.ContainsKey(aDuck.soundType)) {
  898. Debug.LogWarning("You have more than one Duck Group set up with the Sound Group '" + aDuck.soundType + "'. Please delete the duplicates before running again.");
  899. continue;
  900. }
  901. if (aDuck.soundType == VideoPlayerSoundGroupName)
  902. {
  903. Debug.LogError("The specially named Sound Group for Video Players '" + VideoPlayerSoundGroupName + "' cannot be used as a Music Ducking Group. Please remove it.");
  904. continue;
  905. }
  906. duckingBySoundType.Add(aDuck.soundType, aDuck);
  907. }
  908. #if UNITY_2019_3_OR_NEWER && VIDEO_ENABLED
  909. if (videoPlayers.Count > 0)
  910. {
  911. var videoPlayerHolder = VideoPlayerSoundGroupTransform;
  912. if (videoPlayerHolder == null)
  913. {
  914. Debug.LogError("You have deleted the specially named Sound Group for Video Players. Please press stop and open the Master Audio Inspector and expand the Video Player Settings section so it will be automatically created again. Otherwise the audio for your Video Players will not work properly.");
  915. }
  916. else
  917. {
  918. VideoPlayerTrackers.Clear();
  919. for (var i = 0; i < videoPlayers.Count; i++)
  920. {
  921. var aPlayer = videoPlayers[i];
  922. if (aPlayer.clip == null)
  923. {
  924. Debug.LogError("Your clip for Video Player in Game Object '" + aPlayer.name + " is empty. Please assign a video clip or delete this Video Player from Master Audio.");
  925. continue;
  926. }
  927. var childVariationGO = videoPlayerHolder.Find(aPlayer.name);
  928. if (childVariationGO == null)
  929. {
  930. Debug.LogError("You have deleted the one or more Variations in the specially named Video Players Sound Group. Please press stop and open the Master Audio Inspector and expand the Video Player Settings section so it will be automatically created again. Otherwise the audio for your Video Players will not work properly.");
  931. continue;
  932. }
  933. var childVariation = childVariationGO.transform.GetComponent<SoundGroupVariation>();
  934. VideoPlayerTrackers.Add(new VideoPlayerTracker(aPlayer, childVariation));
  935. }
  936. }
  937. var videoPlayerBus = GrabBusByName(VideoPlayerBusName);
  938. if (videoPlayerBus == null)
  939. {
  940. Debug.LogError("You have deleted the specially named Video Players Bus. Please press stop and open the Master Audio Inspector and expand the Video Player Settings section so it will be automatically created again. Otherwise the audio for your Video Players will not work properly.");
  941. }
  942. }
  943. #endif
  944. _soundsLoaded = true;
  945. if (willWarm) {
  946. _warming = true;
  947. var warmGroup = SoundGroupForWarming();
  948. // pre-warm the code so the first sound played for real doesn't have to JIT and be slow.
  949. if (!string.IsNullOrEmpty(warmGroup)) {
  950. var result = PlaySound3DFollowTransform(warmGroup, Trans, 0f);
  951. if (result != null && result.SoundPlayed) {
  952. result.ActingVariation.Stop();
  953. }
  954. }
  955. FireCustomEvent("FakeEvent", _trans);
  956. // ReSharper disable once ForCanBeConvertedToForeach
  957. // Reset stuff for people who use "Save runtime changes".
  958. for (var i = 0; i < customEvents.Count; i++) {
  959. customEvents[i].frameLastFired = -1;
  960. }
  961. frames = 0;
  962. // Event Sounds warmer
  963. // ReSharper disable once ArrangeStaticMemberQualifier
  964. var evts = GameObject.FindObjectsOfType(typeof(EventSounds));
  965. if (evts.Length > 0) {
  966. var evt = evts[0] as EventSounds;
  967. evt.PlaySounds(evt.particleCollisionSound, EventSounds.EventType.UserDefinedEvent);
  968. }
  969. // ReSharper disable once ForCanBeConvertedToForeach
  970. for (var i = 0; i < mutedGroups.Count; i++) {
  971. MuteGroup(mutedGroups[i], false);
  972. }
  973. // done warming
  974. _warming = false;
  975. }
  976. // ReSharper disable once ForCanBeConvertedToForeach
  977. for (var i = 0; i < allVars.Count; i++) {
  978. allVars[i].DisableUpdater();
  979. }
  980. AmbientUtil.InitListenerFollower(); // start this up so it's available to batch occlusion stuff
  981. // fixed: make sure this happens before Playlists start or the volume won't be right.
  982. PersistentAudioSettings.RestoreMasterSettings();
  983. }
  984. // ReSharper disable once UnusedMember.Local
  985. private void Start() {
  986. // wait for Playlist Controller to initialize!
  987. if (musicPlaylists.Count > 0
  988. && musicPlaylists[0].MusicSettings != null
  989. && musicPlaylists[0].MusicSettings.Count > 0
  990. && musicPlaylists[0].MusicSettings[0].clip != null
  991. && PlaylistControllersByName.Count == 0) {
  992. Debug.Log("No Playlist Controllers exist in the Scene. Music will not play.");
  993. }
  994. }
  995. // ReSharper disable once UnusedMember.Local
  996. void OnDisable() {
  997. var sources = GetComponentsInChildren<AudioSource>().ToList();
  998. StopTrackingRuntimeAudioSources(sources);
  999. }
  1000. // ReSharper disable once UnusedMember.Local
  1001. void Update() {
  1002. frames++;
  1003. // adjust for Inspector realtime slider.
  1004. PerformOcclusionFrequencyChanges();
  1005. PerformBusFades();
  1006. PerformBusPitchGlides();
  1007. PerformGroupFades();
  1008. PerformGroupPitchGlides();
  1009. PerformDelayedAmbientTriggers();
  1010. RefillInactiveGroupPools();
  1011. FireCustomEventsWaiting();
  1012. #if UNITY_2019_3_OR_NEWER && VIDEO_ENABLED
  1013. TrackVideoPlayers();
  1014. #endif
  1015. #if ADDRESSABLES_ENABLED
  1016. CheckAddressablesForDelayedRelease();
  1017. #endif
  1018. }
  1019. // ReSharper disable once UnusedMember.Local
  1020. void LateUpdate() {
  1021. if (variationFollowerType != VariationFollowerType.LateUpdate) {
  1022. return;
  1023. }
  1024. ManualUpdate();
  1025. }
  1026. // ReSharper disable once UnusedMember.Local
  1027. void FixedUpdate() {
  1028. if (variationFollowerType != VariationFollowerType.FixedUpdate) {
  1029. return;
  1030. }
  1031. ManualUpdate();
  1032. }
  1033. private void ManualUpdate() {
  1034. RecalcClosestColliderPositions();
  1035. AmbientUtil.ManualUpdate();
  1036. UpdateActiveVariations();
  1037. }
  1038. /*! \cond PRIVATE */
  1039. public string SoundGroupForWarming() {
  1040. string firstGroupName = null;
  1041. for (var k = 0; k < Trans.childCount; k++) {
  1042. var parentGroup = Trans.GetChild(k);
  1043. if (parentGroup.name == AmbientUtil.FollowerHolderName) {
  1044. continue; // don't pick this, not a Sound Group
  1045. }
  1046. if (firstGroupName == null) {
  1047. firstGroupName = parentGroup.name;
  1048. }
  1049. for (var i = 0; i < parentGroup.childCount; i++) {
  1050. var variationGO = parentGroup.GetChild(i);
  1051. var variation = variationGO.GetComponent<SoundGroupVariation>();
  1052. if (variation == null) {
  1053. continue;
  1054. }
  1055. if (variation.audLocation == AudioLocation.Clip) {
  1056. return parentGroup.name;
  1057. }