Changelog
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[6.0.0] - 2021-12-31
This update is a big refresh of API. Most notably, everything that was too transparent/magical has been reworked to be more to-the-face. (How it looks in Resources/Introloop
, how it magically look for nearby AudioSource
to be the template, etc.)
There are quite a lot of breaking changes especially involving everything that was required to be put in Resources
folder. Almost an entire documentation website has been rewritten to teach the new API. Now Introloop has 0 mention to Resources
folder. Welcome to the future.
Lastly, minimum version has been moved to 2019.4 LTS. Asset Store is now moving along with editor's LTS cycle, only accepting new submission on the lowest LTS and accepting updates from the most recent fallen off LTS.
2018 LTS is already going away, and next in queue is 2019 LTS. I had also evaluated that 2019 LTS is the best version for publishing packages right now. Therefore, Introloop is moving to minimum 2019 LTS in this version.
Added
- "Template Source" field is now visible in
IntroloopPlayer
Inspector and is also serialized. Previously, the documentation instructs user to just add anAudioSource
to the same game object asIntroloopPlayer
for the script to transparently pick it up. I feel that is too magical, it is better to be explicit. - Added new
Play
overload withoutIntroloopAudio
in the argument. This overload instead uses connectedIntroloopAudio
in the inspector instead. (Also a new feature.) These are also great withUnityEvent
, a slot to connectIntroloopAudio
will appear in the Inspector when connected. - "Default Introloop Audio" field added and exposed in Inspector. Allowing you to pre-connect
IntroloopAudio
which is used when callingPlay
overload without inputIntroloopAudio
in the arguments. DefaultIntroloopAudio
property getter/setter added. This works likeClips
ofAudioSource
, setting it then callingPlay
next to play it.IntroloopAudioUnityEvent
class added, which is just a subclass ofUnityEvent<IntroloopAudio>
. It can be used to perform dynamic invoke withIntroloopAudio
specified from script.- All samples are upgraded to 2019.4 LTS, getting rid of all deprecated components.
- All samples are refactored and now better for learning.
- Offline documentation and website update.
- Huge code documentation refactor and formatting. (Notably, it is using
<para></para>
correctly. No more wall of texts.)
Changed
- "Template Source" and "Singleton Instance" wording has been used more consistently in the documentation and code. They are also made more visible to the programmer, such as template source now being shown on Inspector instead of silently finding the component to be a template.
- Singleton instance reading
AudioSource
configuration from prefab inResources/Introloop
folder with a specific name has been deprecated. Instead, call the new APIIntroloopPlayer.SetSingletonInstanceTemplateSource(AudioSource templateSource)
before the first access to the singleton instance. The old way was too magical with too many magic assets that can't be moved involved, nowadays with Addressable Asset System, I don't want to touchResources
folder anymore if possible. ApplyAudioSourceCharacteristics
onIntroloopPlayer
is renamed to justApplyAudioSource
.- Subclassed
IntroloopPlayer
now use the same nameInstance
property to get its singleton instance, instead ofGet
. - Minimum version is now 2019.4 LTS. Being at modern LTS version positions this plugin better for the future.
IntroloopPlayer
now can be created without right clicking onAudioClip
. The created asset has emptyAudioClip
slot. Documentation now explain that you need to connect the clip on your own after creating it.IntroloopPlayer
is now created with[CreateAssetMenu]
instead of manualScriptableObject
instantiation, possible because we are not supporting version 2017 anymore.package.json
manifestauthor
section changed to be consistent with my other products. (Unity Package Manager group assets under the sameauthor
in the same dropdown.)
Fixed
- Fixed a bug which
IntroloopPlayer.ApplyAudioSourceCharacteristics
does not correctly copy curve version of spatial blend, reverb zone mix, and spread. (The hard constant values version overwritten the curve copy.) - Fix errors in "IntroloopDemo-Positional" sample scene.
Removed
public static InstantiatePlayer<T>
inIntroloopPlayer
becameprivate protected
.
[5.0.1] - 2019-11-25
I made a mistake of not including documentation and samples in the previous version, the Asset Store Tools can't include unimported folders. They are now included in .zip
form instead, but you need to unzip them manually until Asset Store Tools could publish hidden folders.
[5.0.0] - 2019-11-25
Major SemVer version bump means a breaking change!
Added
AudioSource
characteristic inheritance
It is now possible to setup 4 AudioSource
to the same character, using 1 template AudioSource
attached on the same object as the IntroloopPlayer
. If you can set the template AudioSource
before the player's Start()
, the player inherit the template AudioSource
's almost all settings. If you are late, you can still do it again with the new IntroloopPlayer.ApplyAudioSourceCharacteristics(audioSource)
with an argument of IntroloopPlayer.TemplateSource
(also new).
Settings not inherited (not a "characteristic") :
volume
,pitch
andclip
, because they depends on what you play.loop
, Introloop manages looping with scheduling and not this loop checkbox.playOnAwake
, It does not make sense since you must play though Introloop's player.
This approach allows local IntroloopPlayer
to be more intuitively pre-setup in the scene. You can adjust all aspects of AudioSource
on the template component to be propagated to the underlying 4 AudioSource
at runtime. The template AudioSource
will be disabled after it finished its job. It will feel like you are controling a single AudioSource
instead of 4 it actually uses at runtime.
This inheritance includes AudioMixerGroup
on the template AudioSource
, therefore the "Route To Mixer Group" that was once available on IntroloopPlayer
has been removed in favor of specifying the mixer via template AudioSource
instead.
You may want to revisit all your template prefabs in Resources/Introloop/
. because if you upgraded from pre-5.0.0, they will not retroactively has AudioSource
on them. When in this situation it is assumed that the template AudioSource
is a completely 2D one with no mixer. At least you will want to re-assign the AudioMixerGroup
on the template AudioSource
instead. The website has been updated for this change, you can read everything again in the web.
This affects how any singleton player will be instantiated too :
- It look for
Resources/Introloop/[CLASSNAME].prefab
(e.g.CLASSNAME
=IntroloopPlayer
or your subclass ofIntroloopPlayer<T>
)- If exists,
Instantiate
that, along withAudioSource
template that maybe with it.- Check if the instantiated game object is missing
AudioSource
or not (the templateAudioSource
), it will be added with "good settings" (spatialBlend = 0
andpriority = 0
) if not.
- Check if the instantiated game object is missing
- If not, instantiate one new game object with player component plus
AudioSource
with "good settings". (spatialBlend = 0
andpriority = 0
)
- If exists,
- The next
Start()
of that newly spawned player will populate its real 4AudioSource
and follow inheritance procedure.
Template prefab in Resources/Introloop
now optional
When using IntroloopPlayer.Instance.Play
it is now able to spawn a player with template AudioSource
(that was explained above) with "good settings" (spatialBlend = 0
and priority = 0
) on its own. Of course you can still put a prefab named Resources/Introloop/IntroloopPlayer.prefab
and have it Instantiated
to override this behaviour. That "good settings" will then be inherited following the new inheritance system. Though if you use AudioMixerGroup
, you will want to have a template prefab anyways.
This is mainly to make a package has no more need to mess with your Resources
folder on install. Moving forward as UPM-compatible package this design is important. One demo require it though, so the demo will ask you to create Resources/Introloop/
manually and copy a required template prefab to it.
Unity Package Manager compatible Samples
Demo
folder is now renamed to Samples~
, which is now hidden from Unity importer until you need it. Various demo audio inside now properly not imported to your game. In this new convention, samples are imported to your project by a button in the Package Manager instead of being there from the start.
All 3 samples are moved to this format.
Others
- Added
Play(introloopAudio)
overload explicitly (without fade and start time) for support with Unity editor which could handle only at most 1 argument. (e.g.UnityEvent
delegate connecting) Documentation~
hidden folder added to the package. It contains a Markdown version of the official website http://exceed7.com/introloop.- New website design : http://exceed7.com/introloop. It is a big deal since now the website linked to
Documentation~
hidden folder in this package rather than having to handcraft it separately. Even thisCHANGELOG.md
turns into a webpage, along with API documentation that is the same as code documentation in this package. Now you can read documentation in their Markdown form inDocumentation~
or in the website. It will also reduce my maintenance burden, things in the web will stay up to date with the package 100%. Awesome! - Exposed
Loop
property onIntroloopAudio
. Together withIntroloopAudio.ClipLength
, if the audio'sLoop
isfalse
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.) Existence of "IsPlaying
" state inIntroloopPlayer
is weirdly lacking because scheduling methods inside wrecksAudioSource
play state that they could not be trusted 100%. - Exposed the underlying
AudioClip
onIntroloopAudio
. - Exposed the
Pitch
value onIntroloopAudio
. (Read-only) - Rewrite many method's code documentation. (Also reflected in the API page in the web.)
- Right click menu to create
IntroloopAudio
is now in one more level :Create/Introloop/IntroloopAudio
instead ofCreate/IntroloopAudio
.
Changed
MakeSingletonInstance
static
method onIntroloopPlayer
renamed toInstantiatePlayer
. It was misleading that when you call it the singleton instance stays the same, it is not really related to the singleton at all. Rather, the singleton instance system internally utilize this method to create a player and make that a singleton. This naming is now more descriptive that you can also use it for your own purpose. This is a breaking change but fortunately should be easy to fix as it is just the name.IntroloopSettings
class now madeinternal
as it comes with theIntroloopPlayer
. Also a breaking change but you should not be using them directly in the first place.
Removed
Discontinued support for 2017.1.5f1.
Now the lowest supported version is 2017.4.34f1. (The lowest possible LTS in Unity Hub.)
Trivia : 2017.1.5f1 didn't have Assembly Definition Files, supporting that version make it very difficult to utilize internal
keyword together with [InternalsVisibleTo]
as it doesn't work with Editor
magic folder.
Others
- Offline zipped website documentation removed in favor of Markdown documentation in
Documentation~
folder. - Removed methods on the
IntroloopPlayer
:SetAudioSourceCurveType
,MatchAudioSourceCurveType
,Set2DSpatialBlend
,Set3DSpatialBlend
. They are too specific and in the end cannot freely modify all available things inAudioSource
. Now you should useApplyAudioSourceCharacteristics
to copy everything at once from anyAudioSource
. The methodSetMixerGroup
is left available though so you could individually assign just the mixer.
[4.1.0] - 2019-08-15
Added
Seek(elapsedTime)
This new method internally implemented as calling Play
with custom startTime
. Except that you do not need to specify which audio because it remembered your previous audio. Also it only works if the state is playing. From the outside, it looks like you are "seeking" the playhead.
Introloop naturally has infinite timeline. The time specified here is instead an elapsed time, Introloop will make the current position like you have played by this amount of time, according to your loop boundaries.
It is added as a part of 2019.1+ hack to fix Unity bug, which requires remembering the audio and play again. But I guess it may be handy, so I decided to make it public.
Dirty hack to fix 2019.1+ bug added into all IntroloopPlayer
There is a bug in 2019.1+ where on game minimize, on pressing pause button in editor, or pausing AudioListener, all ScheduledEndTime
will be lost. I confirmed it is not a problem in 2018.4 LTS. And it obviously affects Introloop.
There is an OnApplicationPause(bool)
added to IntroloopPlayer
to fix this while waiting for Unity team to address the bug for real. I have reported since 2019.1 release, as of now that 2019.2 has been released plus the old version is at 2019.1.14f1, it was not fixed yet. I think I couldn't wait for another half a year, so I decided to bake the workaround into the code.
The ideal fix is to call Pause
just before the game goes to minimize then Resume
after we comeback to reschedule. However at OnApplicationPause
, Pause
does not work, as all audio are already on its way to pausing. (Introloop need to see which one is playing to resume correctly.)
So an another approach is that we will remember the time just before the pause, and the play again after coming back using that time. This hack does that, using the new Seek
added in this same update, and it is wrapped in #if UNITY_2019_1_OR_NEWER
. Also, OnApplicationPause
do not get called in editor when pressing pause button (near the play mode button) so you will still experience this bug in the editor. In real device it should take care of the bug.
Track that bug : https://fogbugz.unity3d.com/default.asp?1151637_4i53coq9v07qctp1
Changed
Pause and Stop overloads changed to be able to take action in the same frame
- Previous behaviour :
Pause()
orStop()
without any fade time specified will results in a small pop removal fade time added to reduce harsh noise when pausing. - Previous behaviour :
Pause(-1)
orStop(-1)
with negative fade time results in 0 fade time, however it needs at least the next frame to actually pause or stop since it uses the same routine as the one with fade time. - Previous behaviour :
Pause(0)
orStop(0)
with zero fade time will results in a small pop removal fade time added to reduce harsh noise when pausing. - Previous behaviour :
Pause(555)
orStop(555)
with positive fade time will fade to pause or fade to stop as expected.
There is a problem that no matter what you cannot stop an audio in the same frame that you tell it to. This may cause problem in some situation where you want to free up memory and cannot wait any frame.
- New behaviour :
Pause()
orStop()
without any fade time specified enters a special routine that instantly pause or stop in the same frame. - New behaviour :
Pause(-1)
orStop(-1)
with negative fade time enters a special routine that instantly pause or stop in the same frame. - New behaviour :
Pause(0)
orStop(0)
with zero fade time will results in a small pop removal fade time added to reduce harsh noise when pausing. (unchanged) - New behaviour :
Pause(555)
orStop(555)
with positive fade time will fade to pause or fade to stop as expected. (unchanged)
To use the old pop removal fade time pause or stop, now the only way is that you have to use the overload with fade time, but instead input 0. It means you wants "virtually no fade" but also you don't want a noise problem. It is now a little different from the overload without any argument which do it instantly.
Fixed
- Priority of audio sources generated are now 0 (the highest). Since it is very likely that you do not want any other sound effects to cut off the music when virtual audio sources run out.
- Time method used inside
IntroloopTrack.cs
wasTime.time
. It is nowTime.unscaledTime
so it could accommodate games with dynamically scaled time.
[4.0.0] - 2019-06-15
It's been a year since the last update. Thank you for all your supports so far! As per SemVer, big version change introduce breaking changes.
Known issues
Do not use AudioListener.pause
to pause, the documentation said that it would preserve scheduling status, but what I found is that sometimes it deletes PlaySchedule
on coming back from pause. I have submitted as a bug report which you could track here if it was resolved or not. If possible, stop completely and resume by playing anew with the new startTime
parameter.
Added
Unity Package Manager & Assembly Definition File
The package has been conformed to the recent Unity practice of putting packages.json
and properly using asmdef
file. In the future when Asset Store became integrated with UPM, Introloop is now ready for that.
This also allows you to put the Introloop package outside your Unity project, instead anywhere in your harddisk, then use the local UPM feature to share a single Introloop sources over multiple projects, thanks to packages.json
file.
This is a breaking change if you are also using asmdef
file, because you now have to link up your asmdef
with Introloop's E7.Introloop
.
The assembly definitions will also prevents scripts in the intro scene from getting into your game as well, since you could link to only E7.Introloop
and not E7.Introloop.Demo
. In the future when incremental compiler works better and we could enter play mode without assembly reload, this would help your game even more.
Script icons added
IntroloopAudio
icon is provided so you could discern them from other ScriptableObject
files.
IntroloopPlayer
icon is also added, which could show up in the scene since it is a MonoBehaviour
. If you don't like this, you can click on "Gizmos" in your Scene tab, and then left click on the icon image, not on the little arrow or the checkbox. It will be greyed out and disappear from the scene view.
An Affinity Designer project files are even included, so you could modify some part of the icon if you don't like them!
startTime
optional parameter added to IntroloopPlayer.Play
This is one of the most requested feature. You are now able to specify start time when starting introloop audio.
The time you specify here is called "playhead time". Since IntroloopAudio
conceptually has infinite length, any number that is over looping boundary will be wrapped over to the intro boundary in the calculation.
In effect, you can never specify start time so that it starts after looping boundary.
GetPlayheadTime()
added to IntroloopPlayer
You can also ask the currently playing IntroloopPlayer
the "playhead time". This is not accumulated, the time could decrease when it goes over looping boundary back to intro boundary. So you better visualize it as an actual playhead that jumps back on loop.
This number is usable with the new start time option. Together, you can implement something like remembering field music time and enter battle scene with an another Introloop music completely replaced the field music. When you come back, you can use the remembered "playhead time" to start the field music from the same place. It is a good idea to start from the beginning if the player had passed through some "check point" like a cutscene or entered town, otherwise continue with the playhead time.
Changed
IntroloopSettings
is now the field of IntroloopPlayer
rather than a separated component
That was a stupid design, but I was afraid to let that go.
It will cause missing script on your prefab template in your Resources/Introloop/
folder, please go remove the missing component which was IntroloopSettings
and assign those values in IntroloopPlayer
instead. Luckily the values are just a mixer and fade time, but sorry for inconvenience.
A bit of prepare time added to immediate Play() method
Theoretically playing "instantly" is impossible, even though we want it to play "right now" on calling IntroloopPlayer.Play()
. So this time is the near-instant future that we told the scheduling method to start. With this, it could get better chance that the first loop is accurate at the expense of some waiting time.
(But Introloop was never meant to be a low latency solution, for that please search for Native Audio.)
Previously, you could randomly get a bit off first loop depending on whether it could fulfill this "right now" or not. You notice that usually all loops except the first one are accurate, that's because it got time far into the future as a schedule, unlike the first play.
The default number 0.02f
is choosen to be a bit more that 16ms, the time per 1 frame on 60 FPS. So that it could get through a busy frame.
Removed
Default fade length removed from IntroloopSettings
I have decided that default fade length should be something coming from your game, rather than the task of Introloop. Along with this...
All _Fade
variants of Play
, Stop
, Pause
, Resume
removed
There is now just 1 version of each. Now the normal version handles fade by using the optional fade parameter. Enter 0 or not using the parameter to get the usual "pop removal fade length". If you use negative number, the action is immediate without pop removal fade length.
If you was using default fade length with Fade
variants, make your own default fade length variable coming from the game and input it into the fade parameter of each method.
Dropped support for Unity 5.5.6 and 5.6.5
I used to maintain support far back to those versions. It's time to move on! Even though nothing prevents Introloop from working on those versions, project downgrading is getting more and more difficult.
The new support ranges is as follows : Oldest version in Unity Hub, latest LTS version, latest TECH version, and latest beta version will be tested. As of current, they are : 2017.1.5f1, 2017.4.26f1, 2018.3.14f1, 2019.1.1f1.
Fixed
Streaming load type is now much more accurate when used with Introloop
The mentioned prepare time is the final key to use Streaming
audio load type with Introloop! Because streaming audio size is technically so small that it tries to start instantly, it conflicts with the scheduled start time that is too immediate to fulfill. I observed that with this small delay, Streaming
audio's first loop became much more reliable. Took me long enough to figure this one out. Now, enjoy your memory-conscious introlooped music.
Suppressed unassigned variables warnings
I put pragma warning disable 0649
on several places. No idea how I didn't think of doing this before after all these years. (Sorry)
Code documentation + website improved
I have rewritten everything, and also some XML tag that links code references together are added.
[3.0.0] - 2018-05-20
Added
Multiple singleton players
With IntroloopPlayer.Instance.Play
, it refers to the same "Instance" throughout your game. Meaning that you cannot have 2 concurrent Introloop player playing+looping at the same time.
With MySubClassOfIntroloopPlayer.Get
, it will spawns different set of player. This means you can now have many Introloop playing at the same. It is useful for dividing the players into several parts. Like BGMPlayer, AmbientPlayer, etc.
Moreover, you can then define your own methods on your subclass to be more suitable for your game. Like FieldBGMPlayer.Get.PlayDesertTheme()
instead of IntroloopPlayer.Instance.Play(desertTheme);
.
The template's name was hardcoded as the same as your class name. If your class name is FieldBGMPlayer then you must have FieldBGMPlayer.prefab in the same location as IntroloopPlayer.prefab in Resources folder. (Defined in IntroloopSettings.cs constant fields.)
See the new IntroloopDemoSubclass demo scene for how this works.
IntroloopPlayer.InternalAudioSources property
You can foreach
on this property to make changes to all 4 AudioSource
that Introloop uses at once.
You should not use this in Awake
, as Introloop might be still not yet spawn the AudioSource
.
Local Introloop
You can now have non-static IntroloopPlayer anywhere in the scene as many instances as you like.
If you do, you need to keep and access it with normal IntroloopPlayer
variable.
How to get one is easy, just attach IntroloopPlayer
(and optionally with IntroloopSetting
) to one of your game object.
All of the required AudioSource
will be spawned directly as a child of this game object.
Or an anothey way, you could gameObject.AddComponent<IntroloopPlayer>()
anytime. The next frame all of the required audio sources
will be ready for play.
These local Introloop does not automatically get DontDestroyOnLoad
like IntroloopPlayer.Instance
or Subclass.Get
ones,
thus they will stop playing if you change scene with LoadSceneMode.Single
, etc. Also, it will be positional by default. (Spatial blend is 1, or full 3D.)
Positional Introloop
The point of having a local Introloop is that you would like it to be positional. Imagine you have 10 bushes with an individual seamlessly looping leaf sound. And you also want these to get louder as the player approaches it.
You could spawn a local Introloop and position them on each bush. Note that each one will uses 4 AudioSource
.
The following functions was added :
IntroloopPlayer.Set/MatchAudioSourceCurve
- Set audio curve with AnimationCurve
or copy audio curvesfrom an another AudioSource
.
IntroloopPlayer.Set2DSpatialBlend
- IntroloopPlayer.Instance
and Subclass.Get
automatically get this. Hear full sound independent of AudioListener
position.
IntroloopPlayer.Set3DSpatialBlend
- Local Introloop automatocally get this. Introloop is now dependent on positioning.
IntroloopPlayer.SetSpatialBlend
- Set spatial blend to any number to make it semi 2D-3D.
See the new IntroloopDemoLocalPositional demo scene for how this works.
Changed
- Introloop now use a
namespace
coding practice like all other plugins. Thenamespace
isusing E7.Introloop
. - Minimum supported version now moved to 2017.1.3f1, the lowest version available on Unity Hub. Technically, it should still mostly works from 5.0 onwards but I won't be checking integrity on those versions anymore.
- When right click creating
IntroloopAudio
file, it was atAsset > Create IntroloopAudio
but now it is now properly inAsset > Create > IntroloopAudio
. - Some class which is not intended for you to use has been changed to
internal
.
[2.0.0] - 2017-04-14
Added
Pitch
You can now specify a pitch in an IntroloopAudio asset file. If you would like to use multiple pitches of the same audio, you can just copy the asset file and have different pitches. It can reference to the same actual audio file. Works fine with pause, resume, automatic memory management.
Preload
A feature where critical precision of starting an Introloop Audio is needed. Load the audio by calling IntroloopAudio.Instance.Preload(yourIntroloopAudio) beforehand to pre-consume memory, and then call Play as usual afterwards.
Ogg Streaming as Introloop on iOS/Android
In Unity 5.5.2 they added support for choosing "Streaming" with OGG on iOS/Android. I am happy to inform that this option works with Introloop. Everthing will be the same except it will not cost you as much memory of an entire audio on Play as before in an exchange for some CPU workload.
[1.0.0] - 2015-12-15
Added
It's the first version!