// -------------------------------------------------------------------------------------------------------------------- // // Copyright (c) VRMADA, All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- using System; using System.Threading; using System.Threading.Tasks; using UltimateXR.Extensions.System; using UltimateXR.Extensions.System.Threading; using UnityEngine; using Object = UnityEngine.Object; namespace UltimateXR.Extensions.Unity.Audio { /// /// extensions. /// public static class AudioSourceExt { #region Public Types & Data /// /// Default spatial blend for 3D positioned audio. /// public const float SpatialBlend3D = 0.9f; #endregion #region Public Methods /// /// Ubiquitously plays an . /// /// /// This function creates an but automatically disposes of it once the clip has finished /// playing. /// /// Reference to the sound clip file that will be played. /// How loud the sound is at a distance of one world unit (one meter) [0.0, 1.0]. /// Delay time specified in seconds. /// /// Amount of change in pitch due to slowdown/speed up of the Audio Clip. Value 1 is normal playback /// speed. /// /// Start offset in seconds /// The just created temporal . public static AudioSource PlayClip(AudioClip clip, float volume = 1.0f, float delay = 0.0f, float pitch = 1.0f, float offsetSeconds = 0.0f) { if (!Application.isPlaying) { throw new InvalidOperationException("Playback is only allowed while playing."); } clip.ThrowIfNull(nameof(clip)); volume = Mathf.Clamp01(volume); pitch = Mathf.Clamp01(pitch); var gameObject = new GameObject($"{nameof(AudioSourceExt)}_{nameof(PlayClip)}_{clip.name}"); var audioSource = gameObject.AddComponent(); audioSource.clip = clip; audioSource.volume = volume; audioSource.pitch = pitch; audioSource.spatialBlend = SpatialBlendUbiquitous; if (offsetSeconds - delay >= clip.length) { audioSource.Stop(); Object.Destroy(gameObject, 1.0f); return audioSource; } if (delay > offsetSeconds) { audioSource.PlayDelayed(delay - offsetSeconds); } else { audioSource.Play(); audioSource.time = offsetSeconds - delay; } float duration = (delay + clip.length - offsetSeconds) * (Time.timeScale < 0.00999999977648258 ? 0.01f : Time.timeScale); Object.Destroy(gameObject, duration); return audioSource; } /// /// Plays an AudioClip at a given position in world space. /// /// /// This function creates an but automatically disposes of it once the clip has finished /// playing. /// /// Reference to the sound clip file that will be played. /// Position in world space from which sound originates. /// How loud the sound is at a distance of one world unit (one meter) [0.0, 1.0]. /// Delay time specified in seconds. /// /// Amount of change in pitch due to slowdown/speed up of the Audio Clip. Value 1 is normal playback /// speed. /// /// Sets how much the 3D engine has an effect on the audio source [0.0, 1.0]. /// Start offset in seconds /// The just created temporal . /// public static AudioSource PlayClipAtPoint(AudioClip clip, Vector3 point, float volume = 1.0f, float delay = 0.0f, float pitch = 1.0f, float spatialBlend = SpatialBlend3D, float offsetSeconds = 0.0f) { if (!Application.isPlaying) { throw new InvalidOperationException("Playback is only allowed while playing."); } clip.ThrowIfNull(nameof(clip)); volume = Mathf.Clamp01(volume); spatialBlend = Mathf.Clamp01(spatialBlend); var gameObject = new GameObject($"{nameof(AudioSourceExt)}_{nameof(PlayClipAtPoint)}_{clip.name}") { transform = { position = point } }; var audioSource = gameObject.AddComponent(); audioSource.clip = clip; audioSource.volume = volume; audioSource.pitch = pitch; audioSource.spatialBlend = spatialBlend; if (offsetSeconds - delay >= clip.length) { audioSource.Stop(); Object.Destroy(gameObject, 1.0f); return audioSource; } audioSource.Play(); offsetSeconds = Mathf.Max(offsetSeconds, 0.0f); if (delay > offsetSeconds) { audioSource.PlayDelayed(delay - offsetSeconds); } else { audioSource.Play(); audioSource.time = offsetSeconds - delay; } float duration = (delay + clip.length - offsetSeconds) * (Time.timeScale < 0.00999999977648258 ? 0.01f : Time.timeScale); Object.Destroy(gameObject, duration); return audioSource; } /// /// Asynchronous and ubiquitously plays an . /// /// /// This function creates an but automatically disposes of it once the clip has finished /// playing. /// /// Reference to the sound clip file that will be played. /// How loud the sound is at a distance of one world unit (one meter) [0.0, 1.0]. /// Delay time specified in seconds. /// /// Amount of change in pitch due to slowdown/speed up of the Audio Clip. Value 1 is normal playback /// speed. /// /// Start offset in seconds /// to stop playing. /// An awaitable . public static async Task PlayClipAsync(AudioClip clip, float volume = 1.0f, float delay = 0.0f, float pitch = 1.0f, float offsetSeconds = 0.0f, CancellationToken ct = default) { if (ct.IsCancellationRequested) { return; } if (!Application.isPlaying) { throw new InvalidOperationException("Playback is only allowed while playing."); } if (offsetSeconds >= clip.length) { return; } offsetSeconds = Mathf.Max(offsetSeconds, 0.0f); float duration = (delay + clip.length - offsetSeconds) * (Time.timeScale < 0.00999999977648258 ? 0.01f : Time.timeScale); AudioSource audioSource = PlayClip(clip, volume, delay, pitch, offsetSeconds); await TaskExt.Delay(duration, ct); if (ct.IsCancellationRequested && audioSource != null) { audioSource.Stop(); Object.Destroy(audioSource.gameObject); } } /// /// Asynchronously plays an at a given position in world space. /// /// /// This function creates an but automatically disposes of it once the clip has finished /// playing. /// /// Reference to the sound clip file that will be played. /// Position in world space from which sound originates. /// How loud the sound is at a distance of one world unit (one meter) [0.0, 1.0]. /// Delay time specified in seconds. /// /// Amount of change in pitch due to slowdown/speed up of the Audio Clip. Value 1 is normal playback /// speed. /// /// Sets how much the 3D engine has an effect on the audio source [0.0, 1.0]. /// Start offset in seconds /// to stop playing. /// An awaitable . public static async Task PlayClipAtPointAsync(AudioClip clip, Vector3 point, float volume = 1.0f, float delay = 0.0f, float pitch = 1.0f, float spatialBlend = SpatialBlend3D, float offsetSeconds = 0.0f, CancellationToken ct = default) { if (ct.IsCancellationRequested) { return; } if (!Application.isPlaying) { throw new InvalidOperationException("Playback is only allowed while playing."); } if (offsetSeconds >= clip.length) { return; } offsetSeconds = Mathf.Max(offsetSeconds, 0.0f); float duration = (delay + clip.length - offsetSeconds) * (Time.timeScale < 0.00999999977648258 ? 0.01f : Time.timeScale); AudioSource audioSource = PlayClipAtPoint(clip, point, volume, delay, pitch, spatialBlend, offsetSeconds); await TaskExt.Delay(duration, ct); if (ct.IsCancellationRequested && audioSource != null) { audioSource.Stop(); Object.Destroy(audioSource.gameObject); } } #endregion #region Private Types & Data /// /// Spatial blend for ubiquitous playback. /// private const float SpatialBlendUbiquitous = 0f; #endregion } }