Exemplo n.º 1
0
        internal void Play(IntroloopAudio music, bool isFade)
        {
            if (music == null)
            {
                throw new IntroloopException("Played IntroloopAudio is null!");
            }

            pauseDspTimestamp = 0;
            pauseDspTotal     = 0;

            twoSources[0].pitch = music.Pitch;
            twoSources[1].pitch = music.Pitch;

            AudioDataLoadState loadState = music.audioClip.loadState;
            string             musicName = music.audioClip.name;

            FadeVolume = isFade ? 0 : 1;
            if (loadState != AudioDataLoadState.Loaded)
            {
                IntroloopLog("\"" + musicName + "\" not loaded yet. Loading into memory...");
                StartCoroutine(LoadAndPlayRoutine(music));
            }
            else
            {
                IntroloopLog("\"" + musicName + "\" already loaded in memory.");
                SetupPlayingSchedule(music);
            }
        }
Exemplo n.º 2
0
        private IEnumerator LoadAndPlayRoutine(IntroloopAudio music)
        {
            string musicName        = music.audioClip.name;
            float  startLoadingTime = Time.time;
            float  endLoadingTime;

            music.audioClip.LoadAudioData();
            while (music.audioClip.loadState != AudioDataLoadState.Loaded && music.audioClip.loadState != AudioDataLoadState.Failed)
            {
                yield return(null);
            }
            if (music.audioClip.loadState == AudioDataLoadState.Loaded)
            {
                endLoadingTime = Time.time;
                if (music.audioClip.loadInBackground)
                {
                    IntroloopLog(musicName + " loading successful. (Took " + (endLoadingTime - startLoadingTime) + " seconds loading in background.)");
                }
                else
                {
                    IntroloopLog(musicName + " loading successful.");
                }
                SetupPlayingSchedule(music);
            }
            else
            {
                IntroloopLogError(musicName + " loading failed!");
            }
        }
Exemplo n.º 3
0
        public static IntroloopAudio CreateIntroloopAudioAsset(string fileName, AudioClip audioClip)
        {
            IntroloopAudio asset = ScriptableObject.CreateInstance <IntroloopAudio>();

            asset.audioClip = audioClip;
            asset.Volume    = 1;

            string path = AssetDatabase.GetAssetPath(Selection.activeObject);

            if (path == "")
            {
                path = "Assets";
            }
            else if (Path.GetExtension(path) != "")
            {
                path = path.Replace(Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject)), "");
            }

            string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(path + "/" + fileName + ".asset");

            AssetDatabase.CreateAsset(asset, assetPathAndName);

            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
            EditorUtility.FocusProjectWindow();
            Selection.activeObject = asset;

            return(asset);
        }
Exemplo n.º 4
0
        private void SetupPlayingSchedule(IntroloopAudio music)
        {
            IntroloopLog("Playing \"" + music.audioClip.name + "\"");

            musicAboutToPlay = music;

            musicAboutToPlay = null;
            this.music       = music;
            ApplyVolume();
            nextSourceToPlay = 0;
            isPausing        = false;

            twoSources[0].clip = music.audioClip;
            twoSources[1].clip = music.audioClip;

            //Essential to cancel the Pause
            Stop();

            //PlayScheduled does not reset the playhead!
            twoSources[0].time = 0;
            twoSources[1].time = music.LoopBeginning; //Waiting at the intro part so it will go looping..

            double absoluteTimeNow = AudioSettings.dspTime;

            if (music.nonLooping)
            {
                PlayScheduled(0, absoluteTimeNow);
                IntroloopLog("Type : Non-looping");
            }
            else if (music.loopWholeAudio)
            {
                PlayScheduled(0, absoluteTimeNow);
                SetScheduledEndTime(0, absoluteTimeNow + music.ClipLength);
                introFinishDspTime = absoluteTimeNow + music.ClipLength;

                PlayScheduled(1, absoluteTimeNow + music.ClipLength);
                nextScheduleTime = absoluteTimeNow + music.ClipLength * 2;

                IntroloopLog("Type : Loop whole audio");
            }
            else
            {
                PlayScheduled(0, absoluteTimeNow);
                SetScheduledEndTime(0, absoluteTimeNow + music.IntroLength);
                introFinishDspTime = absoluteTimeNow + music.IntroLength;

                //Followed by looping track

                //If has intro but no looping, music will end and won't loop
                //But in this case it goes to looping track
                PlayScheduled(1, absoluteTimeNow + music.IntroLength);
                nextScheduleTime = (absoluteTimeNow + music.IntroLength) + (music.LoopingLength);
                IntroloopLog("Type : Introloop");
            }
            playDspTimestamp  = absoluteTimeNow;
            pauseDspTimestamp = 0;
            pauseDspTotal     = 0;
            isPlaying         = true;
        }
Exemplo n.º 5
0
 internal void Unload()
 {
     music.Unload();
     IntroloopLog(String.Format("Unloaded \"{0}\" from memory.", music.audioClip.name));
     twoSources[0].clip = null;
     twoSources[1].clip = null;
     music            = null;
     musicAboutToPlay = null;
 }
Exemplo n.º 6
0
        /// <summary>
        /// Play an <see cref="IntroloopAudio"> asset. It applies <see cref="IntroloopAudio.Volume"> and <see cref="IntroloopAudio.Pitch"> to the underlying sources.
        /// If an another <see cref="IntroloopAudio"> is playing on this player, it could cross-fade between the two if <paramref name="fadeLengthSeconds"> is provided.
        /// The faded out audio will be unloaded automatically once the fade is finished.
        /// </summary>
        /// <param name="audio"> An <see cref="IntroloopAudio"> asset file to play.</param>
        /// <param name="fadeLengthSeconds">Fade in/out length to use in seconds. If 0, it uses a small pop removal fade time. If negative, it is immediate. The audio will be unloaded only after it had fade out completely.</param>
        /// <param name="startTime">Specify starting point in time instead of start from the beginning. The time you specify here will be converted to "playhead time", Introloop will make the playhead at the point in time as if you had played for this amount of time before starting. Since <see cref="IntroloopAudio"> conceptually has infinite length, any number that is over looping boundary will be wrapped over to the intro boundary in the calculation. (Except that if the audio is non-looping) The time specified here is not taking <see cref="IntroloopAudio.Pitch"> into account. It's an elapsed time as if <see cref="IntroloopAudio.Pitch"> is 1. </param>
        public void Play(IntroloopAudio audio, float fadeLengthSeconds = 0, float startTime = 0)
        {
            //Auto-crossfade old ones. If no fade length specified, a very very small fade will be used to avoid pops/clicks.
            Stop(fadeLengthSeconds);

            int next = (currentTrack + 1) % 2;

            twoTracks[next].Play(audio, fadeLengthSeconds == 0 ? false : true, startTime);
            towardsVolume[next] = 1;
            fadeLength[next]    = TranslateFadeLength(fadeLengthSeconds);

            currentTrack      = next;
            this.previousPlay = audio;
        }
        internal void Play(IntroloopAudio music, bool isFade, float startTime)
        {
            pauseDspTimestamp = 0;
            pauseDspTotal     = 0;

            twoSources[0].pitch = music.Pitch;
            twoSources[1].pitch = music.Pitch;

            AudioDataLoadState loadState = music.AudioClip.loadState;
            string             musicName = music.AudioClip.name;

            FadeVolume = isFade ? 0 : 1;
            if (loadState != AudioDataLoadState.Loaded)
            {
                IntroloopLog("\"" + musicName + "\" not loaded yet. Loading into memory...");
                StartCoroutine(LoadAndPlayRoutine(music, startTime));
            }
            else
            {
                IntroloopLog("\"" + musicName + "\" already loaded in memory.");
                SetupPlayingSchedule(music, startTime);
            }
        }
        private void SetupPlayingSchedule(IntroloopAudio music, float startTime)
        {
            IntroloopLog("Playing \"" + music.AudioClip.name + "\"");

            musicAboutToPlay = music;

            musicAboutToPlay = null;
            this.music       = music;
            ApplyVolume();
            nextSourceToPlay = 0;
            isPausing        = false;

            twoSources[0].clip = music.AudioClip;
            twoSources[1].clip = music.AudioClip;

            //Essential to cancel the Pause
            Stop();

            //It is important to "anchor" this somewhere and not calling AudioSettings.dspTime again later.
            //Because its value changes even in-between lines of code.
            double dspTimeNow = AudioSettings.dspTime + smallPrepareTime;

            if (music.nonLooping)
            {
                if (startTime < music.UnscaledClipLength)
                {
                    twoSources[0].time = startTime;
                    twoSources[1].time = 0;

                    //Note : you cannot just pull the value back from `twoSources[0].time`. The setter is not instantaneous!
                    startPlayingUnscaledClipTime = startTime;

                    PlayScheduled(0, dspTimeNow);
                }
                else
                {
                    //Do not **actually play** if specified time overshoot.
                    //It could produce this error :
                    //`./Modules/Audio/Public/sound/SoundChannel.cpp(341) : Error executing result (An invalid seek position was passed to this function. )`
                    //The status turned into "play", but it is as if it had already finished.

                    twoSources[0].time           = 0;
                    twoSources[1].time           = 0;
                    startPlayingUnscaledClipTime = music.UnscaledClipLength;
                }
                IntroloopLog("Type : Non-looping");
            }
            else if (music.loopWholeAudio)
            {
                //This is just a simple loop at the end, but achieved with 2 sources in Introloop style.
                //Yes it is an overkill.. but to streamline the process.
                //(Also when in some case Unity 1-source loop is not seamless, you could try this)

                //PlayScheduled does not reset the playhead!

                float loopedStartTime = startTime % music.UnscaledClipLength;
                twoSources[0].time = loopedStartTime;
                //Always wait at the beginning regardless of intro boundary set.
                twoSources[1].time = 0;

                //For end time we need to scale, but also minus out the start time from the clip time, that is scaled too.
                double dspEndMusicTime = dspTimeNow + music.ClipLength - (startTime / music.Pitch);
                PlayScheduled(0, dspTimeNow);
                SetScheduledEndTime(0, dspEndMusicTime);
                introFinishDspTime = dspEndMusicTime;

                PlayScheduled(1, dspEndMusicTime);
                nextTransitionTime           = dspEndMusicTime + music.ClipLength;
                startPlayingUnscaledClipTime = loopedStartTime;

                IntroloopLog("Type : Loop whole audio");
            }
            else
            {
                bool beforeIntro = startTime < music.UnscaledIntroLength;

                if (beforeIntro)
                {
                    //This is affected by pitch, since it will be used on pause & stuff that happen after play.
                    //Dont forget to minus out the start time that take up the intro length.
                    double dspIntroSeamTime = dspTimeNow + music.IntroLength - (startTime / music.Pitch);

                    //The start time is in "playhead time", we make it into regular AudioClip time.
                    twoSources[0].time = startTime;

                    //The 2nd source will wait at the intro part so it will go looping.
                    twoSources[1].time = music.UnscaledIntroLength;

                    PlayScheduled(0, dspTimeNow);
                    SetScheduledEndTime(0, dspIntroSeamTime);
                    introFinishDspTime = dspIntroSeamTime;

                    PlayScheduled(1, dspIntroSeamTime);
                    nextTransitionTime           = dspIntroSeamTime + music.LoopingLength;
                    startPlayingUnscaledClipTime = startTime;
                }
                else
                {
                    //The start time is still the "elapsed looping time", we make it into regular AudioClip "playhead" time. Make sure it never overshoot.
                    float introloopedStartTime = music.UnscaledIntroLength + ((startTime - music.UnscaledIntroLength) % music.UnscaledLoopingLength);

                    //This is for the AudioClip starting time where it has no idea about the pitch.
                    float  unscaledTimeIntoTheLoop = introloopedStartTime - music.UnscaledIntroLength;
                    float  scaledTimeIntoTheLoop   = unscaledTimeIntoTheLoop / music.Pitch;
                    float  timeRemainingInTheLoop  = music.LoopingLength - scaledTimeIntoTheLoop;
                    double dspLoopEndTime          = dspTimeNow + timeRemainingInTheLoop;

                    twoSources[0].time = music.UnscaledIntroLength + unscaledTimeIntoTheLoop;
                    //If start time is over intro, the 2nd source must be waiting at the end instead.
                    twoSources[1].time = music.UnscaledIntroLength + music.UnscaledLoopingLength;

                    //This breaks our "schedule automatically half way until the loop point" policy,
                    //since it is now possible that we start way after that. The solution is to schedule next loop
                    //immediately, and change the state so that it looks like we had played through the intro.
                    PlayScheduled(0, dspTimeNow);

                    nextTransitionTime = dspLoopEndTime;

                    //As if the stitch had occured once.
                    nextSourceToPlay = 1;
                    ScheduleNextLoop();

                    //Intro would have already finished in this case. Time go backwards. This variable is required to make pause work.
                    introFinishDspTime = dspTimeNow - scaledTimeIntoTheLoop;

                    startPlayingUnscaledClipTime = music.UnscaledIntroLength + unscaledTimeIntoTheLoop;;
                }
                IntroloopLog("Type : Introloop");
            }

            playDspTimestamp  = dspTimeNow;
            pauseDspTimestamp = 0;
            pauseDspTotal     = 0;
            isPlaying         = true;
        }
 /// <summary>
 /// An experimental feature in the case that you really want the audio to start
 /// in an instant you call <see cref="Play(IntroloopAudio, float, float)"/>. You must use the same
 /// <see cref="IntroloopAudio"/> that you preload in the next play.
 /// </summary>
 /// <remarks>
 /// By normally using <see cref="Play(IntroloopAudio, float, float)"/> and <see cref="Stop(float)"/>
 /// it loads the audio the moment you called <see cref="Play(IntroloopAudio, float, float)"/>.
 ///
 /// Introloop waits for an audio to load before playing with a coroutine.
 /// (If you have <see cref="AudioClip.loadInBackground"/> in the import settings,
 /// otherwise <see cref="Play(IntroloopAudio, float, float)"/> will be a blocking call)
 ///
 /// Introloop can't guarantee that the playback will be instant but your game can continue while it is loading.
 ///
 /// By using this method before actually calling <see cref="Play(IntroloopAudio, float, float)"/>
 /// it will instead be instant. This function is special that even songs with <see cref="AudioClip.loadInBackground"/>
 /// can be loaded in a blocking fashion. (You can put <see cref="Play(IntroloopAudio, float, float)"/> immediately
 /// in the next line expecting a fully loaded audio)
 ///
 /// However be aware that memory is managed less efficiently in the following case :
 ///
 /// Normally Introloop immediately unloads the previous track to minimize memory,
 /// but if you use <see cref="Preload(IntroloopAudio)"/> then  did not call
 /// <see cref="Play(IntroloopAudio, float, float)"/> with the same <see cref="IntroloopAudio"/> afterwards,
 /// the loaded memory will be unmanaged.
 ///
 /// (Just like if you tick <see cref="AudioClip.preloadAudioData"/> on your clip and have them
 /// in a hierarchy somewhere, then did not use it.)
 ///
 /// Does not work with <see cref="AudioClipLoadType.Streaming"/> audio loading type.
 /// </remarks>
 public void Preload(IntroloopAudio audio)
 {
     audio.Preload();
 }
 /// <summary>
 /// Play an <see cref="IntroloopAudio"/> asset.
 /// Works like <see cref="Play(IntroloopAudio, float, float)"/> but this allows it to be use as a delegate target in editor.
 ///
 /// The requirement for that is there must be at most 1 argument. Therefore this way you cannot
 /// specify fade length or start time.
 /// </summary>
 /// <remarks>
 /// For example you can connect any <see cref="UnityEngine.Events.UnityEvent"/> and select `Play(IntroloopAudio)`
 /// from the drop down, then you will see a slot of `IntroloopAudio` to connect the asset.
 ///
 /// It applies <see cref="IntroloopAudio.Volume"/> and <see cref="IntroloopAudio.Pitch"/>
 /// to the underlying <see cref="AudioSource"/>.
 ///
 /// If an another <see cref="IntroloopAudio"/> is playing on this player,
 /// it could cross-fade between the two if <paramref name="fadeLengthSeconds"/> is provided.
 /// </remarks>
 /// <param name="audio"> An <see cref="IntroloopAudio"/> asset file to play.</param>
 public void Play(IntroloopAudio audio)
 {
     Play(audio, fadeLengthSeconds: 0, startTime: 0);
 }