// --------------------------------------------------------------------------------------------------------------------
//
// 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
}
}