/*
/// Copyright (c) 2015 Sirawat Pitaksarit, Exceed7 Experiments LP
/// http://www.exceed7.com/introloop
*/
using UnityEngine;
namespace E7.Introloop
{
///
/// An asset file for use with .
/// Contains an information where should the audio loop so the player can schedule the seams ahead of time.
///
[CreateAssetMenu(menuName = "Introloop/Introloop Audio")]
public class IntroloopAudio : ScriptableObject
{
[SerializeField] [Range(0, 1)]
[Tooltip(
"Regular AudioClip couldn't alter volume per-audio, but rather must be the task of AudioSource. " +
"Introloop controls 4 AudioSources and allows it to control volume as well.\n\n" +
"This is useful on composing music too, so you could master the song and maximize the volume " +
"regardless of genre, and mix it later to reasonable level here.")]
private float volume = 1;
[SerializeField] [Range(0.1f, 3)]
[Tooltip(
"Introloop couldn't change pitch in real time because that will throw off the schedule, " +
"however, by pre-specifying the pitch, it is possible to scales the schedule accordingly. " +
"The audio stitching will still be on time.")]
// Trust me, even with non-realtime pitch change it is major PITA to finally
// get this working with everything else...
private float pitch = 1;
[SerializeField] internal AudioClip audioClip;
[SerializeField] [PositiveFloat] private float introBoundary;
[SerializeField] [PositiveFloat] private float loopingBoundary;
[SerializeField] internal bool nonLooping;
[SerializeField] internal bool loopWholeAudio;
///
/// The underlying that this asset uses.
///
public AudioClip AudioClip => audioClip;
///
/// Length of this audio if played without looping, already taken account of pitch modification
/// setup on the asset. (Unity's "pitch" is not a real pitch shifting, it is just a play speed change
/// that in turn affects pitch.)
///
///
///
/// Together with , if the audio's Loop is false then you can predict
/// that audio should end after that much time passed.
///
///
/// This allows you to implement an audio queue in the case of playing non-looping audio that
/// wants to follow with something else afterwards.
/// (e.g. inn music while sleeping then continue the old ones which is set to introloop.)
///
///
/// You may not be expecting something like IsPlaying property to be lacking in IntroloopPlayer.
/// Because scheduling methods inside wrecks AudioSource play state that they could not be trusted 100%.
/// For instance, a scheduled-to-play audio is already considered "playing" while actually it is not.
///
///
public float ClipLength => audioClip.length / pitch;
///
/// If the asset is set to either Introloop or Loop button in the editor, this is `true`.
///
///
/// If this is `false`, can predict when the audio would end after played.
///
public bool Loop => !nonLooping;
///
/// Set the underlying to this volume on play.
///
///
/// This allows a per-music volume adjustment. The composer can master/maximize the volume from his DAW
/// without worry about game's context. The developer can drop the volume down after importing on their own.
/// Resulting in a happy game studio.
///
public float Volume
{
get => volume;
set => volume = value;
}
///
/// Read-only pitch settings that you have set on the asset file.
///
///
///
/// Introloop does not allow pitch change other than at the asset, since it will wreck the scheduling if
/// that happen while things are playing. Scheduling needs predictability to plan ahead of time and
/// pitch change will invalidates many assumptions.
///
///
/// (For instance, I schedule "halfway ahead of the seam" and that will not work if suddenly
/// you decided to change the pitch right in front of the seam.)
///
///
/// Also Unity's "pitch" wording in their is technically incorrect.
/// It is rather "speed" that affects pitch in the end. It is not a real pitch shifting.
/// The wording here follows what Unity used rather than technical correctness.
///
///
public float Pitch => pitch;
internal float IntroLength => introBoundary / pitch;
internal float LoopingLength => (loopingBoundary - introBoundary) / pitch;
///
/// This is for timing the seam between intro and looping section instead of IntroLength on start playing.
/// It intentionally does not get divided by pitch. Unity's audio position is not affected by pitch.
///
internal float UnscaledClipLength => audioClip.length;
///
/// This is for timing the seam between intro and looping section instead of IntroLength on start playing.
/// It intentionally does not get divided by pitch. Unity's audio position is not affected by pitch.
///
internal float UnscaledIntroLength => introBoundary;
///
/// This is for timing the seam between intro and looping section instead of IntroLength on start playing.
/// It intentionally does not get divided by pitch. Unity's audio position is not affected by pitch.
///
internal float UnscaledLoopingLength => loopingBoundary - introBoundary;
internal void Preload()
{
audioClip.LoadAudioData();
}
internal void Unload()
{
audioClip.UnloadAudioData();
}
}
}