/* /// 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(); } } }