mirror of
https://github.com/nothke/quality-control.git
synced 2024-12-23 00:07:28 +00:00
326 lines
9.7 KiB
C#
326 lines
9.7 KiB
C#
|
///
|
||
|
/// NAudio by Nothke
|
||
|
/// Simple clip playing and audio source creation in a single line
|
||
|
///
|
||
|
/// See function summaries and examples for usage
|
||
|
///
|
||
|
|
||
|
/// DEFINES:
|
||
|
///
|
||
|
/// Pooling creates a few sources on start and saves them in a queue, reusing them on every play,
|
||
|
/// so sources are not created and destroyed every time played, freeing GC.
|
||
|
/// If you don't want to use pooling, comment this line:
|
||
|
#define POOLING
|
||
|
|
||
|
/// If you are using native audio spatializer, uncomment following line. It will enable spatialization in sources
|
||
|
//#define ENABLE_SPATIALIZER_API
|
||
|
|
||
|
/// If using Oculus audio - ONSP, uncomment following line. It will add the ONSPAudioSource script to sources
|
||
|
//#define USE_OCULUS_AUDIO
|
||
|
|
||
|
//#define SEEK_NOT_PLAYING_SOURCES
|
||
|
|
||
|
using System.Collections.Generic;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.Audio;
|
||
|
|
||
|
public static class NAudio
|
||
|
{
|
||
|
#if POOLING
|
||
|
public static Queue<AudioSource> sourcePool;
|
||
|
const int POOL_SIZE = 50;
|
||
|
#else
|
||
|
// Just to make sure destroy won't cut the sound short in case of an increased latency
|
||
|
const float DESTROY_AFTER_MARGIN_SECONDS = 0.2f;
|
||
|
#endif
|
||
|
|
||
|
const float DEFAULT_MIN_DISTANCE = 1;
|
||
|
const float DEFAULT_SPREAD = 0;
|
||
|
|
||
|
public static Transform root;
|
||
|
|
||
|
#if POOLING
|
||
|
static AudioSource GetNextSource()
|
||
|
{
|
||
|
AudioSource source = sourcePool.Dequeue();
|
||
|
sourcePool.Enqueue(source);
|
||
|
|
||
|
#if SEEK_NOT_PLAYING_SOURCES
|
||
|
int search = 0;
|
||
|
|
||
|
while (source.isPlaying)
|
||
|
{
|
||
|
source = sourcePool.Dequeue();
|
||
|
sourcePool.Enqueue(source);
|
||
|
|
||
|
|
||
|
if (search >= POOL_SIZE)
|
||
|
{
|
||
|
Debug.LogError("All sounds are playing, increase the pool size");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
search++;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return source;
|
||
|
}
|
||
|
|
||
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||
|
public static void InitializePool()
|
||
|
{
|
||
|
InitializePool(POOL_SIZE);
|
||
|
}
|
||
|
|
||
|
public static void InitializePool(int size)
|
||
|
{
|
||
|
if (sourcePool != null && sourcePool.Count > 0)
|
||
|
{
|
||
|
//Debug.LogWarning("Pool already exists");
|
||
|
|
||
|
foreach (var source in sourcePool)
|
||
|
{
|
||
|
if (source)
|
||
|
{
|
||
|
Object.Destroy(source);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
sourcePool = new Queue<AudioSource>(size);
|
||
|
|
||
|
GameObject rootGO = new GameObject("NAudio_Pool");
|
||
|
root = rootGO.transform;
|
||
|
|
||
|
for (int i = 0; i < size; i++)
|
||
|
{
|
||
|
sourcePool.Enqueue(CreateSource(root));
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/// <summary>
|
||
|
/// Plays a clip at a position with properties
|
||
|
/// </summary>
|
||
|
/// <param name="clip">Clip to play</param>
|
||
|
/// <param name="position">Position at which it will be played</param>
|
||
|
/// <param name="volume"></param>
|
||
|
/// <param name="pitch"></param>
|
||
|
/// <param name="spread">How 'wide' the panning will be in 3d. 0 means it will be heard from only one direction, 180 is all around, 360 is opposite direction.</param>
|
||
|
/// <param name="minDistance">The distance at which source volume will be 1 and falloff. Technically this is the sound intensity.</param>
|
||
|
/// <param name="mixerGroup">Add source to mixer group</param>
|
||
|
/// <param name="parent">A transform to attach source to. Position is still world position before parenting.</param>
|
||
|
/// <returns></returns>
|
||
|
public static AudioSource Play(
|
||
|
this AudioClip clip,
|
||
|
Vector3 position,
|
||
|
float volume = 1,
|
||
|
float pitch = 1,
|
||
|
float spread = DEFAULT_SPREAD,
|
||
|
float minDistance = DEFAULT_MIN_DISTANCE,
|
||
|
AudioMixerGroup mixerGroup = null,
|
||
|
Transform parent = null)
|
||
|
{
|
||
|
if (volume == 0) return null;
|
||
|
if (pitch == 0) return null;
|
||
|
Debug.Assert(clip != null, "No AudioClip was passed");
|
||
|
|
||
|
|
||
|
#if POOLING
|
||
|
if (sourcePool == null)
|
||
|
InitializePool();
|
||
|
|
||
|
AudioSource source = GetNextSource();
|
||
|
if (!source) return null;
|
||
|
|
||
|
GameObject go = source.gameObject;
|
||
|
#else
|
||
|
GameObject go = new GameObject("AudioTemp");
|
||
|
AudioSource source = go.AddComponent<AudioSource>();
|
||
|
#endif
|
||
|
go.transform.position = position;
|
||
|
|
||
|
source.spatialBlend = 1; // Makes the source 3D
|
||
|
source.minDistance = minDistance;
|
||
|
|
||
|
source.loop = false;
|
||
|
source.clip = clip;
|
||
|
|
||
|
source.volume = volume;
|
||
|
source.pitch = pitch;
|
||
|
source.spread = spread;
|
||
|
|
||
|
source.dopplerLevel = 0;
|
||
|
|
||
|
source.outputAudioMixerGroup = mixerGroup;
|
||
|
|
||
|
if (parent)
|
||
|
source.transform.parent = parent;
|
||
|
|
||
|
#if ENABLE_SPATIALIZER_API
|
||
|
source.spatialize = true;
|
||
|
#endif
|
||
|
|
||
|
#if USE_OCULUS_AUDIO && !POOLING
|
||
|
source.gameObject.AddComponent<ONSPAudioSource>().SetParameters(ref source);
|
||
|
#endif
|
||
|
|
||
|
source.Play();
|
||
|
|
||
|
#if !POOLING
|
||
|
// Division by zero prevented at the top of the function
|
||
|
GameObject.Destroy(source.gameObject, clip.length * (1 / pitch) + DESTROY_AFTER_MARGIN_SECONDS);
|
||
|
#endif
|
||
|
|
||
|
return source;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Plays a random AudioClip from an array at a position
|
||
|
/// </summary>
|
||
|
/// <param name="clips">An array of AudioClips</param>
|
||
|
/// <param name="position">Position at which it will be played</param>
|
||
|
/// <param name="volume"></param>
|
||
|
/// <param name="pitch"></param>
|
||
|
/// <param name="spread">How 'wide' the panning will be in 3d. 0 means it will be heard from only one direction, 180 is all around, 360 is opposite direction.</param>
|
||
|
/// <param name="minDistance">The distance at which source volume will be 1 and falloff. Technically this is the sound intensity.</param>
|
||
|
/// <param name="mixerGroup">Add source to mixer group</param>
|
||
|
/// <param name="parent">A transform to attach source to. Position is still world position before parenting.</param>
|
||
|
/// <param name="noRepeatSwap">Swaps the order of array so that the same clip never plays twice in succession. Changes the order of clips in the input array.</param>
|
||
|
/// <returns></returns>
|
||
|
public static AudioSource Play(
|
||
|
this AudioClip[] clips, Vector3 position,
|
||
|
float volume = 1, float pitch = 1,
|
||
|
float spread = DEFAULT_SPREAD,
|
||
|
float minDistance = DEFAULT_MIN_DISTANCE,
|
||
|
AudioMixerGroup mixerGroup = null,
|
||
|
Transform parent = null,
|
||
|
bool noRepeatSwap = false)
|
||
|
{
|
||
|
Debug.Assert(clips != null, "NAudio: Clips array is null");
|
||
|
Debug.Assert(clips.Length != 0, "NAudio: No clips in array");
|
||
|
|
||
|
int i;
|
||
|
if (clips.Length == 0)
|
||
|
{
|
||
|
i = 0;
|
||
|
}
|
||
|
else if (noRepeatSwap)
|
||
|
{
|
||
|
i = Random.Range(1, clips.Length);
|
||
|
var tmp = clips[0];
|
||
|
clips[0] = clips[i];
|
||
|
clips[i] = tmp;
|
||
|
i = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
i = Random.Range(0, clips.Length);
|
||
|
}
|
||
|
|
||
|
return Play(clips[i], position, volume, pitch, spread, minDistance, mixerGroup, parent);
|
||
|
}
|
||
|
|
||
|
#region 2D
|
||
|
|
||
|
static AudioSource _source2D;
|
||
|
static AudioSource source2D
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (!_source2D)
|
||
|
_source2D = CreateSource(spatialBlend: 0, loop: false);
|
||
|
|
||
|
return _source2D;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static AudioSource Play2D(
|
||
|
this AudioClip clip,
|
||
|
float volume = 1)
|
||
|
{
|
||
|
source2D.PlayOneShot(clip, volume);
|
||
|
|
||
|
return source2D;
|
||
|
}
|
||
|
|
||
|
public static AudioSource Play2D(
|
||
|
this AudioClip[] clips, float volume = 1)
|
||
|
{
|
||
|
source2D.PlayOneShot(clips[Random.Range(0, clips.Length)], volume);
|
||
|
|
||
|
return source2D;
|
||
|
}
|
||
|
|
||
|
public static AudioSource Play2D(
|
||
|
this AudioClip[] clips,
|
||
|
float volume = 1, float pitch = 1,
|
||
|
float spread = DEFAULT_SPREAD,
|
||
|
float minDistance = DEFAULT_MIN_DISTANCE,
|
||
|
AudioMixerGroup mixerGroup = null)
|
||
|
{
|
||
|
return Play(clips[Random.Range(0, clips.Length)], Vector3.zero, volume, pitch, spread, minDistance, mixerGroup);
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
// AUDIO SOURCE CREATION
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates an audio source with parameters
|
||
|
/// </summary>
|
||
|
/// <param name="at">Creates a source object as a child to this transform</param>
|
||
|
/// <param name="clip">AudioClip that will be attached to this source and played when Play() is called</param>
|
||
|
/// <param name="volume"></param>
|
||
|
/// <param name="pitch"></param>
|
||
|
/// <param name="loop"></param>
|
||
|
/// <param name="playAtStart"></param>
|
||
|
/// <param name="minDistance"></param>
|
||
|
/// <param name="spread"></param>
|
||
|
/// <param name="spatialBlend"></param>
|
||
|
/// <param name="mixerGroup"></param>
|
||
|
/// <returns></returns>
|
||
|
public static AudioSource CreateSource(
|
||
|
Transform at = null, AudioClip clip = null,
|
||
|
float volume = 1, float pitch = 1,
|
||
|
bool loop = true, bool playAtStart = false,
|
||
|
float minDistance = DEFAULT_MIN_DISTANCE,
|
||
|
float spread = DEFAULT_SPREAD,
|
||
|
float spatialBlend = 1,
|
||
|
AudioMixerGroup mixerGroup = null)
|
||
|
{
|
||
|
GameObject go = new GameObject("AudioLoop");
|
||
|
go.transform.parent = at;
|
||
|
go.transform.localPosition = Vector3.zero;
|
||
|
|
||
|
AudioSource source = go.AddComponent<AudioSource>();
|
||
|
|
||
|
source.loop = loop;
|
||
|
source.clip = clip;
|
||
|
|
||
|
source.volume = volume;
|
||
|
source.spatialBlend = spatialBlend;
|
||
|
source.spread = spread;
|
||
|
source.minDistance = minDistance;
|
||
|
|
||
|
source.playOnAwake = playAtStart;
|
||
|
|
||
|
source.outputAudioMixerGroup = mixerGroup;
|
||
|
|
||
|
#if USE_OCULUS_AUDIO
|
||
|
source.gameObject.AddComponent<ONSPAudioSource>().SetParameters(ref source);
|
||
|
#endif
|
||
|
|
||
|
return source;
|
||
|
}
|
||
|
|
||
|
public static void PlayRandomTime(this AudioSource source)
|
||
|
{
|
||
|
if (source.clip == null) return;
|
||
|
|
||
|
source.time = Random.Range(0, source.clip.length);
|
||
|
source.Play();
|
||
|
}
|
||
|
}
|